@toa.io/core 0.1.0-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.
- package/LICENSE +22 -0
- package/package.json +28 -0
- package/src/assignment.js +22 -0
- package/src/call.js +29 -0
- package/src/cascade.js +32 -0
- package/src/composition.js +26 -0
- package/src/connector.js +122 -0
- package/src/context.js +43 -0
- package/src/contract/conditions.js +21 -0
- package/src/contract/index.js +7 -0
- package/src/contract/reply.js +22 -0
- package/src/contract/request.js +49 -0
- package/src/contract/schemas/error.yaml +7 -0
- package/src/contract/schemas/index.js +7 -0
- package/src/contract/schemas/query.yaml +31 -0
- package/src/discovery.js +33 -0
- package/src/emission.js +23 -0
- package/src/entities/changeset.js +46 -0
- package/src/entities/entity.js +48 -0
- package/src/entities/factory.js +33 -0
- package/src/entities/index.js +5 -0
- package/src/entities/set.js +15 -0
- package/src/event.js +33 -0
- package/src/exceptions.js +88 -0
- package/src/exposition.js +24 -0
- package/src/index.js +43 -0
- package/src/locator.js +49 -0
- package/src/observation.js +19 -0
- package/src/operation.js +61 -0
- package/src/query/criteria.js +41 -0
- package/src/query/options.js +40 -0
- package/src/query.js +36 -0
- package/src/receiver.js +36 -0
- package/src/remote.js +17 -0
- package/src/runtime.js +38 -0
- package/src/state.js +95 -0
- package/src/transition.js +50 -0
- package/src/transmission.js +33 -0
- package/test/call.fixtures.js +25 -0
- package/test/call.test.js +52 -0
- package/test/cascade.fixtures.js +11 -0
- package/test/cascade.test.js +42 -0
- package/test/connector.fixtures.js +40 -0
- package/test/connector.test.js +199 -0
- package/test/contract/conditions.test.js +26 -0
- package/test/contract/contract.fixtures.js +27 -0
- package/test/contract/request.test.js +99 -0
- package/test/emission.fixtures.js +16 -0
- package/test/emission.test.js +35 -0
- package/test/entities/entity.fixtures.js +26 -0
- package/test/entities/entity.test.js +64 -0
- package/test/entities/factory.fixtures.js +18 -0
- package/test/entities/factory.test.js +48 -0
- package/test/entities/set.fixtures.js +11 -0
- package/test/entities/set.test.js +12 -0
- package/test/event.fixtures.js +28 -0
- package/test/event.test.js +106 -0
- package/test/locator.test.js +34 -0
- package/test/query.fixtures.js +100 -0
- package/test/query.test.js +86 -0
- package/test/receiver.fixtures.js +22 -0
- package/test/receiver.test.js +66 -0
- package/test/runtime.fixtures.js +19 -0
- package/test/runtime.test.js +40 -0
- package/test/state.fixtures.js +46 -0
- package/test/state.test.js +54 -0
- package/test/transmission.fixtures.js +15 -0
- package/test/transmission.test.js +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2020-present Artem Gurtovoi
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toa.io/core",
|
|
3
|
+
"version": "0.1.0-dev.0",
|
|
4
|
+
"description": "Toa Core",
|
|
5
|
+
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
6
|
+
"homepage": "https://github.com/toa-io/toa#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/toa-io/toa.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/toa-io/toa/issues"
|
|
13
|
+
},
|
|
14
|
+
"main": "src/index.js",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@rsql/parser": "1.2.4",
|
|
23
|
+
"@toa.io/gears": "^0.1.0-dev.0",
|
|
24
|
+
"@toa.io/schema": "^0.1.0-dev.0",
|
|
25
|
+
"clone-deep": "4.0.1"
|
|
26
|
+
},
|
|
27
|
+
"gitHead": "632df6cead03909ad2cfb8852e178914ac4ab5d2"
|
|
28
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Operation } = require('./operation')
|
|
4
|
+
|
|
5
|
+
class Assignment extends Operation {
|
|
6
|
+
async acquire (scope) {
|
|
7
|
+
scope.subject = this.subject.changeset(scope.request.query)
|
|
8
|
+
scope.state = scope.subject.get()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async commit (scope) {
|
|
12
|
+
const { subject, state, reply } = scope
|
|
13
|
+
|
|
14
|
+
if (reply.error !== undefined) return
|
|
15
|
+
|
|
16
|
+
subject.set(state)
|
|
17
|
+
|
|
18
|
+
await this.subject.apply(subject)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
exports.Assignment = Assignment
|
package/src/call.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Connector } = require('./connector')
|
|
4
|
+
|
|
5
|
+
class Call extends Connector {
|
|
6
|
+
#transmitter
|
|
7
|
+
#contract
|
|
8
|
+
|
|
9
|
+
constructor (transmitter, contract) {
|
|
10
|
+
super()
|
|
11
|
+
|
|
12
|
+
this.#transmitter = transmitter
|
|
13
|
+
this.#contract = contract
|
|
14
|
+
|
|
15
|
+
this.depends(transmitter)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async invoke (request = {}) {
|
|
19
|
+
this.#contract.fit(request)
|
|
20
|
+
|
|
21
|
+
const { exception, ...reply } = await this.#transmitter.request(request)
|
|
22
|
+
|
|
23
|
+
if (exception) throw exception
|
|
24
|
+
|
|
25
|
+
return reply
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
exports.Call = Call
|
package/src/cascade.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { merge } = require('@toa.io/gears')
|
|
4
|
+
const { Connector } = require('./connector')
|
|
5
|
+
|
|
6
|
+
class Cascade extends Connector {
|
|
7
|
+
#bridges
|
|
8
|
+
|
|
9
|
+
constructor (bridges) {
|
|
10
|
+
super()
|
|
11
|
+
|
|
12
|
+
this.#bridges = bridges
|
|
13
|
+
|
|
14
|
+
this.depends(bridges)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async run (...args) {
|
|
18
|
+
const reply = {}
|
|
19
|
+
|
|
20
|
+
for (const bridge of this.#bridges) {
|
|
21
|
+
const partial = await bridge.run(...args)
|
|
22
|
+
|
|
23
|
+
if (partial.error) return { error: partial.error }
|
|
24
|
+
|
|
25
|
+
merge(reply, partial)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return reply
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
exports.Cascade = Cascade
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { console } = require('@toa.io/gears')
|
|
4
|
+
|
|
5
|
+
const { Connector } = require('./connector')
|
|
6
|
+
|
|
7
|
+
class Composition extends Connector {
|
|
8
|
+
constructor (expositions, producers, receivers, extensions) {
|
|
9
|
+
super()
|
|
10
|
+
|
|
11
|
+
this.depends(expositions)
|
|
12
|
+
this.depends(producers)
|
|
13
|
+
this.depends(receivers)
|
|
14
|
+
this.depends(extensions)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async connection () {
|
|
18
|
+
console.info('Composition complete')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async disconnected () {
|
|
22
|
+
console.info('Composition shutdown complete')
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.Composition = Composition
|
package/src/connector.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { console } = require('@toa.io/gears')
|
|
4
|
+
const { newid } = require('@toa.io/gears')
|
|
5
|
+
|
|
6
|
+
class Connector {
|
|
7
|
+
#connectors = []
|
|
8
|
+
#linked = []
|
|
9
|
+
#connecting
|
|
10
|
+
#disconnecting
|
|
11
|
+
|
|
12
|
+
id
|
|
13
|
+
connected = false
|
|
14
|
+
|
|
15
|
+
constructor () {
|
|
16
|
+
this.id = this.constructor.name + '#' + newid().substring(0, 8)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
depends (connector) {
|
|
20
|
+
let next
|
|
21
|
+
|
|
22
|
+
if (connector instanceof Array) {
|
|
23
|
+
connector = connector.filter((item) => item instanceof Connector)
|
|
24
|
+
|
|
25
|
+
if (connector.length > 0) {
|
|
26
|
+
next = new Connector()
|
|
27
|
+
|
|
28
|
+
for (const item of connector) {
|
|
29
|
+
this.#connectors.push(item)
|
|
30
|
+
item.depends(next)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
if (connector instanceof Connector) {
|
|
35
|
+
next = connector
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (next !== undefined) {
|
|
40
|
+
this.#connectors.push(next)
|
|
41
|
+
next.link(this)
|
|
42
|
+
|
|
43
|
+
return next
|
|
44
|
+
} else return this
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
link (connector) {
|
|
48
|
+
this.#linked.push(connector)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async connect () {
|
|
52
|
+
if (this.#connecting) return this.#connecting
|
|
53
|
+
|
|
54
|
+
this.#disconnecting = undefined
|
|
55
|
+
|
|
56
|
+
this.#connecting = (async () => {
|
|
57
|
+
await Promise.all(this.#connectors.map(connector => connector.connect()))
|
|
58
|
+
await this.connection()
|
|
59
|
+
})()
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await this.#connecting
|
|
63
|
+
this.connected = true
|
|
64
|
+
} catch (e) {
|
|
65
|
+
await this.disconnect(true)
|
|
66
|
+
throw e
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.debug(`Connector '${this.id}' connected`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async disconnect (interrupt) {
|
|
73
|
+
if (interrupt !== true) await this.#connecting
|
|
74
|
+
|
|
75
|
+
if (this.#disconnecting) return this.#disconnecting
|
|
76
|
+
|
|
77
|
+
const linked = this.#linked.reduce((acc, parent) => acc || parent.connected, false)
|
|
78
|
+
|
|
79
|
+
if (linked && interrupt !== true) return
|
|
80
|
+
|
|
81
|
+
this.connected = false
|
|
82
|
+
this.#connecting = undefined
|
|
83
|
+
|
|
84
|
+
this.#disconnecting = (async () => {
|
|
85
|
+
const start = +new Date()
|
|
86
|
+
const interval = setInterval(() => {
|
|
87
|
+
const delay = +new Date() - start
|
|
88
|
+
|
|
89
|
+
if (delay > DELAY) console.warn(`Connector ${this.id} still disconnecting (${delay})`)
|
|
90
|
+
}, DELAY)
|
|
91
|
+
|
|
92
|
+
if (interrupt !== true) await this.disconnection()
|
|
93
|
+
|
|
94
|
+
clearInterval(interval)
|
|
95
|
+
|
|
96
|
+
await Promise.all(this.#connectors.map(connector => connector.disconnect()))
|
|
97
|
+
this.disconnected()
|
|
98
|
+
})()
|
|
99
|
+
|
|
100
|
+
await this.#disconnecting
|
|
101
|
+
|
|
102
|
+
console.debug(`Connector '${this.id}' disconnected`)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
debug (node = {}) {
|
|
106
|
+
node[this.id] = { connected: this.connected }
|
|
107
|
+
|
|
108
|
+
if (this.#connectors.length > 0) for (const connector of this.#connectors) connector.debug?.(node[this.id])
|
|
109
|
+
|
|
110
|
+
return node
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async connection () {}
|
|
114
|
+
|
|
115
|
+
async disconnection () {}
|
|
116
|
+
|
|
117
|
+
disconnected () {}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const DELAY = 5000
|
|
121
|
+
|
|
122
|
+
exports.Connector = Connector
|
package/src/context.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Connector } = require('./connector')
|
|
4
|
+
|
|
5
|
+
class Context extends Connector {
|
|
6
|
+
#local
|
|
7
|
+
#discover
|
|
8
|
+
#remotes = {}
|
|
9
|
+
|
|
10
|
+
constructor (local, discover) {
|
|
11
|
+
super()
|
|
12
|
+
|
|
13
|
+
this.#local = local
|
|
14
|
+
this.#discover = discover
|
|
15
|
+
|
|
16
|
+
this.depends(local)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async apply (endpoint, request) {
|
|
20
|
+
return this.#local.invoke(endpoint, request)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async call (domain, name, endpoint, request) {
|
|
24
|
+
const remote = await this.#remote(domain, name)
|
|
25
|
+
|
|
26
|
+
return remote.invoke(endpoint, request)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async #remote (domain, name) {
|
|
30
|
+
if (this.#remotes[domain] === undefined) this.#remotes[domain] = {}
|
|
31
|
+
|
|
32
|
+
if (this.#remotes[domain][name] === undefined) {
|
|
33
|
+
const remote = await this.#discover(domain, name)
|
|
34
|
+
|
|
35
|
+
this.depends(remote)
|
|
36
|
+
this.#remotes[domain][name] = remote
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return this.#remotes[domain][name]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
exports.Context = Context
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { SystemException } = require('../exceptions')
|
|
4
|
+
|
|
5
|
+
class Conditions {
|
|
6
|
+
#schema
|
|
7
|
+
|
|
8
|
+
constructor (schema) {
|
|
9
|
+
this.#schema = schema
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
fit (value) {
|
|
13
|
+
const error = this.#schema.fit(value)
|
|
14
|
+
|
|
15
|
+
if (error !== null) throw new this.constructor.Exception(error)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static Exception = SystemException
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
exports.Conditions = Conditions
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const schemas = require('./schemas')
|
|
4
|
+
const { Conditions } = require('./conditions')
|
|
5
|
+
const { ResponseContractException } = require('../exceptions')
|
|
6
|
+
|
|
7
|
+
class Reply extends Conditions {
|
|
8
|
+
static Exception = ResponseContractException
|
|
9
|
+
|
|
10
|
+
static schema (output, error) {
|
|
11
|
+
const schema = { properties: {}, additionalProperties: false }
|
|
12
|
+
|
|
13
|
+
if (output !== undefined) schema.properties.output = output
|
|
14
|
+
|
|
15
|
+
if (error !== undefined) schema.properties.error = error
|
|
16
|
+
else schema.properties.error = schemas.error
|
|
17
|
+
|
|
18
|
+
return schema
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
exports.Reply = Reply
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const clone = require('clone-deep')
|
|
4
|
+
const schemas = require('./schemas')
|
|
5
|
+
const { RequestContractException } = require('../exceptions')
|
|
6
|
+
const { Conditions } = require('./conditions')
|
|
7
|
+
|
|
8
|
+
class Request extends Conditions {
|
|
9
|
+
static Exception = RequestContractException
|
|
10
|
+
|
|
11
|
+
static schema (definition) {
|
|
12
|
+
const schema = { properties: {}, additionalProperties: false }
|
|
13
|
+
const required = []
|
|
14
|
+
|
|
15
|
+
if (definition.input) {
|
|
16
|
+
definition.input.additionalProperties = false
|
|
17
|
+
schema.properties.input = definition.input
|
|
18
|
+
required.push('input')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (definition.query === true) required.push('query')
|
|
22
|
+
|
|
23
|
+
if (definition.query !== false) {
|
|
24
|
+
const query = clone(schemas.query)
|
|
25
|
+
|
|
26
|
+
if (definition.type === 'observation') {
|
|
27
|
+
delete query.properties.version
|
|
28
|
+
} else {
|
|
29
|
+
delete query.properties.projection
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (definition.type !== 'observation' || definition.subject !== 'set') {
|
|
33
|
+
delete query.properties.omit
|
|
34
|
+
delete query.properties.limit
|
|
35
|
+
} else {
|
|
36
|
+
if (query.required === undefined) query.required = ['limit']
|
|
37
|
+
else query.required.push('limit')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
schema.properties.query = query
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (required.length > 0) schema.required = required
|
|
44
|
+
|
|
45
|
+
return schema
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
exports.Request = Request
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
properties:
|
|
2
|
+
id:
|
|
3
|
+
$ref: https://schemas.toa.io/0.0.0/definitions#/definitions/id
|
|
4
|
+
version:
|
|
5
|
+
type: integer
|
|
6
|
+
minimum: 0
|
|
7
|
+
criteria:
|
|
8
|
+
type: string
|
|
9
|
+
omit:
|
|
10
|
+
type: integer
|
|
11
|
+
minimum: 0
|
|
12
|
+
limit:
|
|
13
|
+
type: integer
|
|
14
|
+
minimum: 0
|
|
15
|
+
sort:
|
|
16
|
+
type: array
|
|
17
|
+
uniqueItems: true
|
|
18
|
+
minItems: 1
|
|
19
|
+
items:
|
|
20
|
+
type: string
|
|
21
|
+
pattern: ^[a-zA-Z]+([-a-zA-Z0-9]*[a-zA-Z0-9]+)?(:(asc|desc))?$
|
|
22
|
+
projection:
|
|
23
|
+
type: array
|
|
24
|
+
uniqueItems: true
|
|
25
|
+
minItems: 1
|
|
26
|
+
items:
|
|
27
|
+
type: string
|
|
28
|
+
pattern: ^([a-zA-Z]+([-a-zA-Z0-9]*[a-zA-Z0-9]+)?)$
|
|
29
|
+
not:
|
|
30
|
+
const: id
|
|
31
|
+
additionalProperties: false
|
package/src/discovery.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Connector } = require('./connector')
|
|
4
|
+
|
|
5
|
+
class Discovery extends Connector {
|
|
6
|
+
#lookup
|
|
7
|
+
#lookups
|
|
8
|
+
|
|
9
|
+
constructor (lookup) {
|
|
10
|
+
super()
|
|
11
|
+
|
|
12
|
+
this.#lookup = lookup
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async connection () {
|
|
16
|
+
this.#lookups = {}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async lookup (locator) {
|
|
20
|
+
const id = locator.id
|
|
21
|
+
|
|
22
|
+
if (this.#lookups[id] === undefined) {
|
|
23
|
+
this.#lookups[id] = await this.#lookup(locator)
|
|
24
|
+
this.depends(this.#lookups[id])
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const { output } = await this.#lookups[id].invoke()
|
|
28
|
+
|
|
29
|
+
return output
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
exports.Discovery = Discovery
|
package/src/emission.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Connector } = require('./connector')
|
|
4
|
+
|
|
5
|
+
class Emission extends Connector {
|
|
6
|
+
#events
|
|
7
|
+
|
|
8
|
+
constructor (events) {
|
|
9
|
+
super()
|
|
10
|
+
|
|
11
|
+
this.#events = events
|
|
12
|
+
|
|
13
|
+
this.depends(events)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async emit (event) {
|
|
17
|
+
const emission = this.#events.map((e) => e.emit(event))
|
|
18
|
+
|
|
19
|
+
await Promise.all(emission)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
exports.Emission = Emission
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { merge, newid } = require('@toa.io/gears')
|
|
4
|
+
const { EntityContractException } = require('../exceptions')
|
|
5
|
+
|
|
6
|
+
class Changeset {
|
|
7
|
+
query
|
|
8
|
+
|
|
9
|
+
#schema
|
|
10
|
+
#state
|
|
11
|
+
|
|
12
|
+
constructor (schema, query) {
|
|
13
|
+
this.query = query
|
|
14
|
+
|
|
15
|
+
this.#schema = schema
|
|
16
|
+
this.#state = schema.system()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get () {
|
|
20
|
+
return this.#state
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
set (value) {
|
|
24
|
+
const error = this.#schema.match(value)
|
|
25
|
+
|
|
26
|
+
if (error !== null) throw new EntityContractException(error)
|
|
27
|
+
|
|
28
|
+
this.#state = value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export () {
|
|
32
|
+
const changeset = this.#state
|
|
33
|
+
const result = { changeset }
|
|
34
|
+
const insert = merge({ id: newid() }, changeset)
|
|
35
|
+
const error = this.#schema.fit(insert)
|
|
36
|
+
|
|
37
|
+
if (error === null) {
|
|
38
|
+
delete insert.id
|
|
39
|
+
result.insert = merge(insert, changeset, { override: true })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return result
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
exports.Changeset = Changeset
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const clone = require('clone-deep')
|
|
4
|
+
const { difference, newid } = require('@toa.io/gears')
|
|
5
|
+
|
|
6
|
+
const { EntityContractException } = require('../exceptions')
|
|
7
|
+
|
|
8
|
+
class Entity {
|
|
9
|
+
#schema
|
|
10
|
+
#origin = null
|
|
11
|
+
#state
|
|
12
|
+
|
|
13
|
+
constructor (schema, argument) {
|
|
14
|
+
this.#schema = schema
|
|
15
|
+
|
|
16
|
+
if (typeof argument === 'object') {
|
|
17
|
+
this.#state = clone(argument)
|
|
18
|
+
this.#origin = argument
|
|
19
|
+
} else {
|
|
20
|
+
const id = typeof argument === 'string' ? argument : newid()
|
|
21
|
+
this.#state = this.#initial(id)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get () {
|
|
26
|
+
return this.#state
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
set (value) {
|
|
30
|
+
const error = this.#schema.fit(value)
|
|
31
|
+
|
|
32
|
+
if (error) throw new EntityContractException(error)
|
|
33
|
+
|
|
34
|
+
this.#state = value
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
event () {
|
|
38
|
+
return {
|
|
39
|
+
origin: this.#origin,
|
|
40
|
+
state: this.#state,
|
|
41
|
+
changeset: this.#origin === null ? this.#state : difference(this.#origin, this.#state)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#initial = (id) => this.#schema.defaults({ id })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
exports.Entity = Entity
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Entity } = require('./entity')
|
|
4
|
+
const { EntitySet } = require('./set')
|
|
5
|
+
const { Changeset } = require('./changeset')
|
|
6
|
+
|
|
7
|
+
class Factory {
|
|
8
|
+
#schema
|
|
9
|
+
|
|
10
|
+
constructor (schema) {
|
|
11
|
+
this.#schema = schema
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
init (id) {
|
|
15
|
+
return new Entity(this.#schema, id)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
entity (record) {
|
|
19
|
+
return new Entity(this.#schema, record)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set (recordset) {
|
|
23
|
+
const set = recordset.map((record) => this.entity(record))
|
|
24
|
+
|
|
25
|
+
return new EntitySet(set)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
changeset (query) {
|
|
29
|
+
return new Changeset(this.#schema, query)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
exports.Factory = Factory
|