@toa.io/core 1.0.0-alpha.27 → 1.0.0-alpha.272
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/CHANGELOG.md +137 -0
- package/package.json +7 -9
- package/src/assignment.js +3 -2
- package/src/call.js +21 -1
- package/src/cascade.js +7 -5
- package/src/component.js +59 -15
- package/src/composition.js +1 -1
- package/src/connector.js +28 -17
- package/src/context.js +6 -0
- package/src/contract/contract.js +22 -0
- package/src/contract/reply.js +34 -9
- package/src/contract/request.js +19 -5
- package/src/contract/schemas/index.js +1 -0
- package/src/contract/schemas/query.yaml +14 -1
- package/src/contract/schemas/source.yaml +23 -0
- package/src/discovery.js +13 -7
- package/src/effect.js +19 -0
- package/src/entities/changeset.js +5 -8
- package/src/entities/entity.js +74 -24
- package/src/entities/factory.js +17 -6
- package/src/entities/newid.js +11 -0
- package/src/entities/set.js +13 -0
- package/src/event.js +19 -1
- package/src/exceptions.js +26 -19
- package/src/exposition.js +3 -2
- package/src/guard.js +17 -0
- package/src/index.js +8 -0
- package/src/observation.js +1 -9
- package/src/operation.js +38 -8
- package/src/outbox/index.js +6 -0
- package/src/outbox/outbox.js +301 -0
- package/src/query/options.js +3 -2
- package/src/query.js +32 -2
- package/src/receiver.js +73 -11
- package/src/remote.js +8 -7
- package/src/state.js +90 -47
- package/src/transition.js +11 -19
- package/src/transmission.js +12 -3
- package/src/unmanaged.js +11 -0
- package/test/component.test.js +4 -3
- package/test/connector.fixtures.js +8 -0
- package/test/connector.test.js +20 -0
- package/test/contract/conditions.test.js +5 -5
- package/test/contract/request.test.js +46 -7
- package/test/discovery.test.js +56 -0
- package/test/emission.fixtures.js +1 -2
- package/test/entities/entity.test.js +58 -48
- package/test/entities/factory.test.js +5 -3
- package/test/event.test.js +4 -4
- package/test/outbox.test.js +114 -0
- package/test/query.test.js +17 -0
- package/test/receiver.fixtures.js +1 -0
- package/test/state.fixtures.js +11 -8
- package/test/state.test.js +61 -13
- package/types/atomicity.d.ts +58 -0
- package/types/bindings.d.ts +7 -5
- package/types/bridges.ts +3 -0
- package/types/component.d.ts +4 -1
- package/types/context.d.ts +3 -0
- package/types/entity.d.ts +2 -2
- package/types/extensions.d.ts +7 -4
- package/types/index.ts +4 -1
- package/types/message.d.ts +1 -0
- package/types/operations.d.ts +6 -0
- package/types/outbox.d.ts +53 -0
- package/types/query.d.ts +2 -0
- package/types/remote.d.ts +18 -0
- package/types/request.d.ts +15 -0
- package/types/state.d.ts +9 -6
- package/types/storages.d.ts +34 -9
- package/src/contract/conditions.js +0 -21
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Origin of a call, see toa.core.Source.
|
|
2
|
+
# Deliberately permissive on required properties: the contract throws on a mismatch,
|
|
3
|
+
# and a strict union would turn a partially stamped source into a failed business call.
|
|
4
|
+
# `additionalProperties: false` combined with `removeAdditional` is what matters here —
|
|
5
|
+
# it strips whatever a peer puts in, bounding the cardinality of the resulting map.
|
|
6
|
+
type: object
|
|
7
|
+
properties:
|
|
8
|
+
namespace:
|
|
9
|
+
type: string
|
|
10
|
+
maxLength: 64
|
|
11
|
+
component:
|
|
12
|
+
type: string
|
|
13
|
+
maxLength: 64
|
|
14
|
+
operation:
|
|
15
|
+
type: string
|
|
16
|
+
maxLength: 64
|
|
17
|
+
event:
|
|
18
|
+
type: string
|
|
19
|
+
maxLength: 64
|
|
20
|
+
service:
|
|
21
|
+
type: string
|
|
22
|
+
maxLength: 64
|
|
23
|
+
additionalProperties: false
|
package/src/discovery.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { console } = require('
|
|
3
|
+
const { console } = require('openspan')
|
|
4
4
|
const { Connector } = require('./connector')
|
|
5
5
|
|
|
6
6
|
class Discovery extends Connector {
|
|
@@ -25,20 +25,26 @@ class Discovery extends Connector {
|
|
|
25
25
|
this.depends(this.#lookups[id])
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
const since = Date.now()
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// the wait is unbounded by design, as a dependency may still be starting,
|
|
31
|
+
// so repeating is the only way a component stuck on one stays visible
|
|
32
|
+
const warning = setInterval(() => {
|
|
33
|
+
const waiting = Math.round((Date.now() - since) / 1000)
|
|
34
|
+
|
|
35
|
+
console.warn('Waiting for lookup response', { component: id, waiting })
|
|
36
|
+
}, INTERVAL)
|
|
37
|
+
|
|
38
|
+
warning.unref()
|
|
32
39
|
|
|
33
40
|
const output = await this.#lookups[id].invoke()
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
clearTimeout(timeout)
|
|
42
|
+
clearInterval(warning)
|
|
37
43
|
|
|
38
44
|
return output
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
const
|
|
48
|
+
const INTERVAL = 5000
|
|
43
49
|
|
|
44
50
|
exports.Discovery = Discovery
|
package/src/effect.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Observation } = require('./observation')
|
|
4
|
+
|
|
5
|
+
class Effect extends Observation {
|
|
6
|
+
|
|
7
|
+
async acquire (store) {
|
|
8
|
+
const { query, entity, input } = store.request
|
|
9
|
+
|
|
10
|
+
if (entity === undefined)
|
|
11
|
+
return super.acquire(store)
|
|
12
|
+
|
|
13
|
+
store.scope = await this.scope.ensure(query, entity, input)
|
|
14
|
+
store.state = store.scope.get()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
exports.Effect = Effect
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
merge,
|
|
5
|
-
overwrite,
|
|
6
|
-
newid
|
|
7
|
-
} = require('@toa.io/generic')
|
|
8
3
|
const { EntityContractException } = require('../exceptions')
|
|
9
4
|
|
|
10
5
|
class Changeset {
|
|
@@ -18,7 +13,7 @@ class Changeset {
|
|
|
18
13
|
this.query = query
|
|
19
14
|
|
|
20
15
|
this.#schema = schema
|
|
21
|
-
this.#state =
|
|
16
|
+
this.#state = {}
|
|
22
17
|
}
|
|
23
18
|
|
|
24
19
|
get () {
|
|
@@ -26,10 +21,12 @@ class Changeset {
|
|
|
26
21
|
}
|
|
27
22
|
|
|
28
23
|
set (value) {
|
|
29
|
-
const error = this.#schema.
|
|
24
|
+
const error = this.#schema.match(value)
|
|
30
25
|
|
|
31
|
-
if (error !== null)
|
|
26
|
+
if (error !== null)
|
|
27
|
+
throw new EntityContractException(error, value)
|
|
32
28
|
|
|
29
|
+
delete value._version
|
|
33
30
|
value._updated = Date.now()
|
|
34
31
|
|
|
35
32
|
this.#state = value
|
package/src/entities/entity.js
CHANGED
|
@@ -1,24 +1,36 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
|
|
5
|
-
newid
|
|
6
|
-
} = require('@toa.io/generic')
|
|
7
|
-
|
|
8
|
-
const { EntityContractException } = require('../exceptions')
|
|
3
|
+
const { EntityContractException, EntityGuardException } = require('../exceptions')
|
|
4
|
+
const { newid } = require('./newid')
|
|
9
5
|
|
|
10
6
|
class Entity {
|
|
7
|
+
deleted = false
|
|
11
8
|
#schema
|
|
9
|
+
#guards
|
|
12
10
|
#origin = null
|
|
13
11
|
#state
|
|
12
|
+
#mutable = true
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @param {boolean} [mutable] whether the entity may be modified and committed
|
|
16
|
+
*/
|
|
17
|
+
constructor (schema, argument, guards, mutable = true) {
|
|
16
18
|
this.#schema = schema
|
|
19
|
+
this.#guards = guards
|
|
17
20
|
|
|
18
21
|
if (typeof argument === 'object') {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
/*
|
|
23
|
+
* The origin is the pre-image a commit diffs the new state against. An operation
|
|
24
|
+
* that cannot commit has nothing to diff, so it takes the record as it came from
|
|
25
|
+
* the storage instead of paying for a deep copy of every record it read.
|
|
26
|
+
*/
|
|
27
|
+
this.#mutable = mutable
|
|
28
|
+
|
|
29
|
+
if (mutable) {
|
|
30
|
+
this.#set(structuredClone(argument))
|
|
31
|
+
this.#origin = argument
|
|
32
|
+
} else
|
|
33
|
+
this.#set(argument)
|
|
22
34
|
} else {
|
|
23
35
|
const id = argument === undefined ? newid() : argument
|
|
24
36
|
this.#init(id)
|
|
@@ -29,37 +41,75 @@ class Entity {
|
|
|
29
41
|
return this.#state
|
|
30
42
|
}
|
|
31
43
|
|
|
32
|
-
set (value) {
|
|
33
|
-
|
|
44
|
+
set (value, optional = false) {
|
|
45
|
+
if (!this.#mutable)
|
|
46
|
+
throw new Error('Entity acquired by a read-only operation cannot be modified')
|
|
47
|
+
|
|
48
|
+
if (!optional)
|
|
49
|
+
this.#guard(value)
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
const error = optional ? this.#schema.fitOptional(value) : this.#schema.fit(value)
|
|
36
52
|
|
|
53
|
+
if (error !== null)
|
|
54
|
+
throw new EntityContractException(error, value)
|
|
55
|
+
|
|
56
|
+
this.#revive(value)
|
|
37
57
|
this.#set(value)
|
|
38
58
|
}
|
|
39
59
|
|
|
40
|
-
event () {
|
|
60
|
+
event (input = undefined) {
|
|
41
61
|
return {
|
|
42
62
|
origin: this.#origin,
|
|
43
63
|
state: this.#state,
|
|
44
|
-
|
|
64
|
+
trailers: this.#state._trailers,
|
|
65
|
+
input
|
|
45
66
|
}
|
|
46
67
|
}
|
|
47
68
|
|
|
48
69
|
#init (id) {
|
|
49
|
-
const value = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
const value = { id, _version: 0 }
|
|
71
|
+
|
|
72
|
+
this.set(value, true)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#guard (value) {
|
|
76
|
+
if (this.#guards === undefined)
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
for (const guard of this.#guards) {
|
|
80
|
+
const ok = guard.fit(value, this.#origin)
|
|
81
|
+
|
|
82
|
+
if (ok === false)
|
|
83
|
+
throw new EntityGuardException(guard.name, value)
|
|
53
84
|
}
|
|
85
|
+
}
|
|
54
86
|
|
|
55
|
-
|
|
87
|
+
// deletion is only expressed as a new _deleted timestamp,
|
|
88
|
+
// so committing over a tombstone without touching it means revival
|
|
89
|
+
#revive (value) {
|
|
90
|
+
if (this.#origin?._deleted == null || value._deleted !== this.#origin._deleted)
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
value._deleted = null
|
|
94
|
+
this.deleted = false
|
|
56
95
|
}
|
|
57
96
|
|
|
58
97
|
#set (value) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
98
|
+
if (!('_trailers' in value))
|
|
99
|
+
Object.defineProperty(value, '_trailers', {
|
|
100
|
+
writable: false,
|
|
101
|
+
configurable: false,
|
|
102
|
+
enumerable: false,
|
|
103
|
+
value: {}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
if (!('_created' in value)) {
|
|
107
|
+
value._created = Date.now()
|
|
108
|
+
value._updated ??= value._created
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if ('_deleted' in value && value._deleted !== null)
|
|
112
|
+
this.deleted = true
|
|
63
113
|
|
|
64
114
|
if (this.#state !== undefined) {
|
|
65
115
|
value._updated = Date.now()
|
package/src/entities/factory.js
CHANGED
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { newid } = require('./newid')
|
|
3
4
|
const { Entity } = require('./entity')
|
|
4
5
|
const { EntitySet } = require('./set')
|
|
5
6
|
const { Changeset } = require('./changeset')
|
|
6
7
|
|
|
7
8
|
class Factory {
|
|
8
9
|
#schema
|
|
10
|
+
#guards
|
|
9
11
|
|
|
10
|
-
constructor (schema) {
|
|
12
|
+
constructor (schema, guards) {
|
|
11
13
|
this.#schema = schema
|
|
14
|
+
this.#guards = guards
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fit (values) {
|
|
18
|
+
this.#schema.validate({ id: newid(), ...values }, 'Entity')
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
init (id) {
|
|
15
|
-
return new Entity(this.#schema, id)
|
|
22
|
+
return new Entity(this.#schema, id, this.#guards)
|
|
16
23
|
}
|
|
17
24
|
|
|
18
|
-
object (record) {
|
|
19
|
-
return new Entity(this.#schema, record)
|
|
25
|
+
object (record, mutable = true) {
|
|
26
|
+
return new Entity(this.#schema, record, this.#guards, mutable)
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
objects (recordset) {
|
|
23
|
-
const set = recordset.map((record) => this.object(record))
|
|
29
|
+
objects (recordset, init, mutable = true) {
|
|
30
|
+
const set = recordset.map((record) => this.object(record, mutable))
|
|
31
|
+
|
|
32
|
+
if (init !== undefined)
|
|
33
|
+
for (const id of init)
|
|
34
|
+
set.unshift(this.init(id))
|
|
24
35
|
|
|
25
36
|
return new EntitySet(set)
|
|
26
37
|
}
|
package/src/entities/set.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { SystemException } = require("../exceptions")
|
|
4
|
+
|
|
3
5
|
class EntitySet {
|
|
4
6
|
#set
|
|
5
7
|
|
|
@@ -10,6 +12,17 @@ class EntitySet {
|
|
|
10
12
|
get () {
|
|
11
13
|
return this.#set.map((entity) => entity.get())
|
|
12
14
|
}
|
|
15
|
+
|
|
16
|
+
set (values) {
|
|
17
|
+
if (values.length !== this.#set.length)
|
|
18
|
+
throw new SystemException('Objects array must not be modified')
|
|
19
|
+
|
|
20
|
+
values.forEach((value, index) => this.#set[index].set(value))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
events (input = undefined) {
|
|
24
|
+
return this.#set.map((entity) => entity.event(input))
|
|
25
|
+
}
|
|
13
26
|
}
|
|
14
27
|
|
|
15
28
|
exports.EntitySet = EntitySet
|
package/src/event.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { console, current, encode } = require('openspan')
|
|
3
4
|
const { Connector } = require('./connector')
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -12,11 +13,15 @@ class Event extends Connector {
|
|
|
12
13
|
#conditioned
|
|
13
14
|
#subjective
|
|
14
15
|
|
|
16
|
+
/** @type {string} */
|
|
17
|
+
#label
|
|
18
|
+
|
|
15
19
|
constructor (definition, emitter, bridge = undefined) {
|
|
16
20
|
super()
|
|
17
21
|
|
|
18
22
|
this.#conditioned = definition.conditioned
|
|
19
23
|
this.#subjective = definition.subjective
|
|
24
|
+
this.#label = definition.label ?? 'event'
|
|
20
25
|
this.#emitter = emitter
|
|
21
26
|
this.#bridge = bridge
|
|
22
27
|
|
|
@@ -32,7 +37,20 @@ class Event extends Connector {
|
|
|
32
37
|
/** @type {toa.core.Message} */
|
|
33
38
|
const message = { payload }
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
const options = {
|
|
41
|
+
name: `${this.#label} publish`,
|
|
42
|
+
kind: 'producer',
|
|
43
|
+
attributes: { 'messaging.destination.name': this.#label }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await console.span(options, async () => {
|
|
47
|
+
const context = current()
|
|
48
|
+
|
|
49
|
+
if (context !== undefined)
|
|
50
|
+
message.telemetry = encode(context)
|
|
51
|
+
|
|
52
|
+
await this.#emitter.emit(message)
|
|
53
|
+
})
|
|
36
54
|
}
|
|
37
55
|
}
|
|
38
56
|
}
|
package/src/exceptions.js
CHANGED
|
@@ -4,7 +4,6 @@ const { swap } = require('@toa.io/generic')
|
|
|
4
4
|
|
|
5
5
|
const codes = {
|
|
6
6
|
System: 0,
|
|
7
|
-
NotImplemented: 10,
|
|
8
7
|
|
|
9
8
|
Contract: 200,
|
|
10
9
|
RequestSyntax: 201,
|
|
@@ -12,6 +11,7 @@ const codes = {
|
|
|
12
11
|
RequestConflict: 203,
|
|
13
12
|
ResponseContract: 211,
|
|
14
13
|
EntityContract: 212,
|
|
14
|
+
EntityGuard: 213,
|
|
15
15
|
QuerySyntax: 221,
|
|
16
16
|
|
|
17
17
|
State: 300,
|
|
@@ -32,9 +32,12 @@ class Exception {
|
|
|
32
32
|
code
|
|
33
33
|
message
|
|
34
34
|
|
|
35
|
-
constructor (code, message) {
|
|
35
|
+
constructor (code, message, cause) {
|
|
36
36
|
this.code = code
|
|
37
37
|
this.message = message
|
|
38
|
+
|
|
39
|
+
if (cause !== undefined)
|
|
40
|
+
this.cause = cause
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -49,31 +52,30 @@ class SystemException extends Exception {
|
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
class ContractException extends Exception {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
this.keyword = error.keyword
|
|
61
|
-
this.property = error.property
|
|
62
|
-
this.schema = error.schema
|
|
63
|
-
this.path = error.path
|
|
55
|
+
constructor (code, error, cause) {
|
|
56
|
+
super(code || codes.Contract, typeof error === 'string' ? error : error?.message, cause)
|
|
57
|
+
|
|
58
|
+
if (typeof error === 'object' && error !== null)
|
|
59
|
+
for (const k of ['keyword', 'property', 'schema', 'path', 'params'])
|
|
60
|
+
if (k in error)
|
|
61
|
+
this[k] = error[k]
|
|
64
62
|
}
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
class RequestContractException extends ContractException {
|
|
68
|
-
constructor (error) { super(codes.RequestContract, error) }
|
|
66
|
+
constructor (error, cause) { super(codes.RequestContract, error, cause) }
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
class ResponseContractException extends ContractException {
|
|
72
|
-
constructor (error) { super(codes.ResponseContract, error) }
|
|
70
|
+
constructor (error, cause) { super(codes.ResponseContract, error, cause) }
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
class EntityContractException extends ContractException {
|
|
76
|
-
constructor (error) { super(codes.EntityContract, error) }
|
|
74
|
+
constructor (error, cause) { super(codes.EntityContract, error, cause) }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class EntityGuardException extends ContractException {
|
|
78
|
+
constructor (name, cause) { super(codes.EntityGuard, name, cause) }
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
// #region exports
|
|
@@ -82,14 +84,19 @@ exports.SystemException = SystemException
|
|
|
82
84
|
exports.RequestContractException = RequestContractException
|
|
83
85
|
exports.ResponseContractException = ResponseContractException
|
|
84
86
|
exports.EntityContractException = EntityContractException
|
|
87
|
+
exports.EntityGuardException = EntityGuardException
|
|
85
88
|
|
|
86
89
|
for (const [name, code] of Object.entries(codes)) {
|
|
87
90
|
const classname = name + 'Exception'
|
|
88
91
|
|
|
89
92
|
if (exports[classname] === undefined) {
|
|
90
93
|
exports[classname] = class extends Exception {
|
|
91
|
-
constructor (message) {
|
|
92
|
-
|
|
94
|
+
constructor (message, cause) {
|
|
95
|
+
message = message
|
|
96
|
+
? `${classname}: ${message}`
|
|
97
|
+
: classname
|
|
98
|
+
|
|
99
|
+
super(code, message ?? classname, cause)
|
|
93
100
|
}
|
|
94
101
|
}
|
|
95
102
|
}
|
package/src/exposition.js
CHANGED
|
@@ -20,8 +20,9 @@ class Exposition extends Connector {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const expose = (manifest) => {
|
|
23
|
-
const { namespace, name, operations, events
|
|
24
|
-
|
|
23
|
+
const { namespace, name, entity, operations, events } = manifest
|
|
24
|
+
|
|
25
|
+
return { namespace, name, entity, operations, events }
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
exports.Exposition = Exposition
|
package/src/guard.js
ADDED
package/src/index.js
CHANGED
|
@@ -5,11 +5,13 @@ const { Composition } = require('./composition')
|
|
|
5
5
|
const { Connector } = require('./connector')
|
|
6
6
|
const { Context } = require('./context')
|
|
7
7
|
const { Discovery } = require('./discovery')
|
|
8
|
+
const { Effect } = require('./effect')
|
|
8
9
|
const { Emission } = require('./emission')
|
|
9
10
|
const { Event } = require('./event')
|
|
10
11
|
const { Exposition } = require('./exposition')
|
|
11
12
|
const { Locator } = require('./locator')
|
|
12
13
|
const { Observation } = require('./observation')
|
|
14
|
+
const { Outbox } = require('./outbox')
|
|
13
15
|
const { Operation } = require('./operation')
|
|
14
16
|
const { Query } = require('./query')
|
|
15
17
|
const { Receiver } = require('./receiver')
|
|
@@ -19,6 +21,8 @@ const { Component } = require('./component')
|
|
|
19
21
|
const { State } = require('./state')
|
|
20
22
|
const { Transition } = require('./transition')
|
|
21
23
|
const { Transmission } = require('./transmission')
|
|
24
|
+
const { Unmanaged } = require('./unmanaged')
|
|
25
|
+
const { Guard } = require('./guard')
|
|
22
26
|
|
|
23
27
|
exports.entities = require('./entities')
|
|
24
28
|
exports.exceptions = require('./exceptions')
|
|
@@ -32,11 +36,13 @@ exports.Composition = Composition
|
|
|
32
36
|
exports.Connector = Connector
|
|
33
37
|
exports.Context = Context
|
|
34
38
|
exports.Discovery = Discovery
|
|
39
|
+
exports.Effect = Effect
|
|
35
40
|
exports.Emission = Emission
|
|
36
41
|
exports.Event = Event
|
|
37
42
|
exports.Exposition = Exposition
|
|
38
43
|
exports.Locator = Locator
|
|
39
44
|
exports.Observation = Observation
|
|
45
|
+
exports.Outbox = Outbox
|
|
40
46
|
exports.Operation = Operation
|
|
41
47
|
exports.Query = Query
|
|
42
48
|
exports.Receiver = Receiver
|
|
@@ -45,3 +51,5 @@ exports.Remote = Remote
|
|
|
45
51
|
exports.State = State
|
|
46
52
|
exports.Transition = Transition
|
|
47
53
|
exports.Transmission = Transmission
|
|
54
|
+
exports.Unmanaged = Unmanaged
|
|
55
|
+
exports.Guard = Guard
|
package/src/observation.js
CHANGED
|
@@ -3,16 +3,8 @@
|
|
|
3
3
|
const { Operation } = require('./operation')
|
|
4
4
|
|
|
5
5
|
class Observation extends Operation {
|
|
6
|
-
async acquire (store) {
|
|
7
|
-
const scope = await this.query(store.request.query)
|
|
8
|
-
const state = scope === null ? null : scope.get()
|
|
9
|
-
|
|
10
|
-
store.scope = scope
|
|
11
|
-
store.state = state
|
|
12
|
-
}
|
|
13
|
-
|
|
14
6
|
async run (store) {
|
|
15
|
-
if (store.scope === null) store.reply = null
|
|
7
|
+
if (store.scope === null || (store.scope?.deleted === true && store.request.query?.options?.deleted !== true)) store.reply = null
|
|
16
8
|
else await super.run(store)
|
|
17
9
|
}
|
|
18
10
|
}
|
package/src/operation.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { Connector } = require('./connector')
|
|
4
|
-
const { SystemException } = require('./exceptions')
|
|
4
|
+
const { SystemException, RequestContractException } = require('./exceptions')
|
|
5
|
+
const { Readable } = require('node:stream')
|
|
5
6
|
|
|
6
7
|
class Operation extends Connector {
|
|
7
8
|
scope
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Whether what this operation acquires may be modified and committed. Only a
|
|
12
|
+
* transition commits, and only a commit needs the pre-image an entity keeps
|
|
13
|
+
* to diff the new state against.
|
|
14
|
+
*
|
|
15
|
+
* @protected
|
|
16
|
+
*/
|
|
17
|
+
mutable = false
|
|
18
|
+
|
|
9
19
|
#cascade
|
|
10
20
|
#contracts
|
|
11
21
|
#query
|
|
@@ -26,8 +36,15 @@ class Operation extends Connector {
|
|
|
26
36
|
|
|
27
37
|
async invoke (request) {
|
|
28
38
|
try {
|
|
29
|
-
if (request.authentic !== true)
|
|
30
|
-
|
|
39
|
+
if (request.authentic !== true)
|
|
40
|
+
this.#contracts.request.fit(request)
|
|
41
|
+
|
|
42
|
+
if ('query' in request)
|
|
43
|
+
request.query = this.#query.parse(request.query)
|
|
44
|
+
|
|
45
|
+
// validate entity
|
|
46
|
+
if ('entity' in request)
|
|
47
|
+
this.scope.fit(request.entity)
|
|
31
48
|
|
|
32
49
|
const store = { request }
|
|
33
50
|
|
|
@@ -47,14 +64,24 @@ class Operation extends Connector {
|
|
|
47
64
|
return store.reply
|
|
48
65
|
}
|
|
49
66
|
|
|
50
|
-
async acquire () {
|
|
67
|
+
async acquire (store) {
|
|
68
|
+
if (this.#scope === 'none')
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
const scope = await this.query(store.request.query)
|
|
72
|
+
const raw = scope === null || scope instanceof Readable
|
|
73
|
+
|
|
74
|
+
store.scope = scope
|
|
75
|
+
store.state = raw ? scope : scope.get()
|
|
76
|
+
}
|
|
51
77
|
|
|
52
78
|
async run (store) {
|
|
53
79
|
const { request, state } = store
|
|
54
|
-
|
|
55
|
-
const reply = await this.#cascade.run(request.input, state) || {}
|
|
80
|
+
const reply = await this.#cascade.run(request.input, state)
|
|
56
81
|
|
|
57
|
-
//
|
|
82
|
+
// validate reply only on local environments
|
|
83
|
+
if (process.env.TOA_ENV === 'local' && !(reply instanceof Readable))
|
|
84
|
+
this.#contracts.reply.fit(reply)
|
|
58
85
|
|
|
59
86
|
store.reply = reply
|
|
60
87
|
}
|
|
@@ -62,7 +89,10 @@ class Operation extends Connector {
|
|
|
62
89
|
async commit () {}
|
|
63
90
|
|
|
64
91
|
async query (query) {
|
|
65
|
-
|
|
92
|
+
if (query === undefined)
|
|
93
|
+
throw new RequestContractException('Request query is required')
|
|
94
|
+
|
|
95
|
+
return this.scope[this.#scope](query, this.mutable)
|
|
66
96
|
}
|
|
67
97
|
}
|
|
68
98
|
|