@toa.io/core 1.0.0-alpha.28 → 1.0.0-alpha.282
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 +168 -0
- package/package.json +8 -10
- package/src/assignment.js +5 -8
- package/src/call.js +35 -9
- package/src/cascade.js +9 -11
- package/src/component.js +61 -21
- package/src/composition.js +3 -7
- package/src/connector.js +29 -22
- package/src/context.js +8 -6
- package/src/contract/contract.js +18 -0
- package/src/contract/index.js +2 -7
- package/src/contract/reply.js +36 -15
- package/src/contract/request.js +20 -10
- package/src/contract/schemas/index.js +9 -5
- package/src/contract/schemas/query.yaml +14 -1
- package/src/contract/schemas/source.yaml +23 -0
- package/src/discovery.js +15 -13
- package/src/effect.js +15 -0
- package/src/emission.js +2 -6
- package/src/entities/changeset.js +7 -14
- package/src/entities/entity.js +75 -29
- package/src/entities/factory.js +21 -14
- package/src/entities/index.js +1 -5
- package/src/entities/newid.js +9 -0
- package/src/entities/set.js +13 -4
- package/src/event.js +21 -7
- package/src/exceptions.js +53 -39
- package/src/exposition.js +5 -8
- package/src/guard.js +13 -0
- package/src/index.js +28 -47
- package/src/locator.js +2 -6
- package/src/observation.js +3 -15
- package/src/operation.js +40 -14
- package/src/outbox/index.js +1 -0
- package/src/outbox/outbox.js +296 -0
- package/src/query/criteria.js +3 -7
- package/src/query/options.js +5 -8
- package/src/query.js +36 -7
- package/src/receiver.js +76 -18
- package/src/reflection.js +2 -6
- package/src/remote.js +9 -12
- package/src/state.js +89 -50
- package/src/transition.js +13 -25
- package/src/transmission.js +15 -10
- package/src/unmanaged.js +7 -0
- package/test/call.fixtures.js +8 -12
- package/test/call.test.js +21 -9
- package/test/component.fixtures.js +5 -8
- package/test/component.test.js +22 -10
- package/test/connector.fixtures.js +10 -8
- package/test/connector.test.js +59 -38
- package/test/context.fixtures.js +8 -12
- package/test/context.test.js +18 -7
- package/test/contract/conditions.test.js +11 -9
- package/test/contract/contract.fixtures.js +12 -16
- package/test/contract/request.test.js +84 -34
- package/test/discovery.test.js +64 -0
- package/test/emission.fixtures.js +6 -10
- package/test/emission.test.js +29 -12
- package/test/entities/entity.fixtures.js +7 -11
- package/test/entities/entity.test.js +70 -49
- package/test/entities/factory.fixtures.js +22 -10
- package/test/entities/factory.test.js +29 -15
- package/test/entities/set.fixtures.js +6 -8
- package/test/entities/set.test.js +6 -5
- package/test/event.fixtures.js +9 -14
- package/test/event.test.js +57 -31
- package/test/locator.test.js +25 -24
- package/test/outbox.test.js +121 -0
- package/test/query.fixtures.js +1 -5
- package/test/query.test.js +42 -14
- package/test/receiver.fixtures.js +9 -12
- package/test/receiver.test.js +45 -22
- package/test/reflection.test.js +7 -6
- package/test/state.fixtures.js +25 -30
- package/test/state.test.js +72 -19
- package/test/transmission.fixtures.js +4 -6
- package/test/transmission.test.js +33 -15
- package/types/atomicity.d.ts +58 -0
- package/types/bindings.d.ts +8 -6
- package/types/bridges.ts +5 -2
- package/types/component.d.ts +7 -4
- package/types/context.d.ts +6 -3
- package/types/entity.d.ts +4 -4
- package/types/event.d.ts +1 -1
- package/types/extensions.d.ts +35 -10
- package/types/index.ts +16 -13
- 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/receiver.d.ts +2 -2
- package/types/reflection.d.ts +1 -1
- package/types/remote.d.ts +18 -0
- package/types/request.d.ts +16 -1
- package/types/state.d.ts +11 -8
- package/types/storages.d.ts +36 -11
- package/src/contract/conditions.js +0 -21
package/src/contract/reply.js
CHANGED
|
@@ -1,25 +1,46 @@
|
|
|
1
|
-
|
|
1
|
+
import * as schemas from './schemas/index.js'
|
|
2
|
+
import { Contract } from './contract.js'
|
|
3
|
+
import { ResponseContractException } from '../exceptions.js'
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
const { Conditions } = require('./conditions')
|
|
5
|
-
const { ResponseContractException } = require('../exceptions')
|
|
6
|
-
|
|
7
|
-
class Reply extends Conditions {
|
|
5
|
+
export class Reply extends Contract {
|
|
8
6
|
static Exception = ResponseContractException
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
* @returns {toa.schema.JSON}
|
|
12
|
-
*/
|
|
13
|
-
static schema (output, error) {
|
|
8
|
+
static schema (output, errors) {
|
|
14
9
|
const schema = { type: 'object', properties: {}, additionalProperties: false }
|
|
15
10
|
|
|
16
|
-
if (output !== undefined)
|
|
11
|
+
if (output !== undefined) {
|
|
12
|
+
/*
|
|
13
|
+
* A reply carries more than the operation declares — `_version` and the rest of the
|
|
14
|
+
* record's own fields — so what it is validated against is the declaration relaxed.
|
|
15
|
+
* On a copy: the declaration itself is what a component publishes when asked to
|
|
16
|
+
* explain, and relaxing that would publish a contract nobody wrote.
|
|
17
|
+
*/
|
|
18
|
+
output = structuredClone(output)
|
|
19
|
+
|
|
20
|
+
if (output.type === 'object')
|
|
21
|
+
output.additionalProperties = true
|
|
22
|
+
else if (output.type === 'array' && output.items?.type === 'object')
|
|
23
|
+
output.items.additionalProperties = true
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
schema.properties.output = output
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (errors !== undefined)
|
|
29
|
+
schema.properties.error = {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
code: {
|
|
33
|
+
enum: errors
|
|
34
|
+
},
|
|
35
|
+
message: {
|
|
36
|
+
type: 'string'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
required: ['code']
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
schema.properties.error = schemas.error
|
|
20
43
|
|
|
21
44
|
return schema
|
|
22
45
|
}
|
|
23
46
|
}
|
|
24
|
-
|
|
25
|
-
exports.Reply = Reply
|
package/src/contract/request.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
import * as schemas from './schemas/index.js'
|
|
2
|
+
import { RequestContractException } from '../exceptions.js'
|
|
3
|
+
import { Contract } from './contract.js'
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
export class Request extends Contract {
|
|
6
|
+
/** @readonly */
|
|
7
|
+
discovery = {}
|
|
8
|
+
|
|
9
|
+
constructor (schema, definition) {
|
|
10
|
+
super(schema)
|
|
11
|
+
|
|
12
|
+
for (const key of ['input', 'output', 'errors'])
|
|
13
|
+
if (definition[key] !== undefined)
|
|
14
|
+
this.discovery[key] = definition[key]
|
|
15
|
+
}
|
|
6
16
|
|
|
7
|
-
class Request extends Conditions {
|
|
8
17
|
static Exception = RequestContractException
|
|
9
18
|
|
|
10
19
|
/**
|
|
@@ -13,7 +22,11 @@ class Request extends Conditions {
|
|
|
13
22
|
static schema (definition, entity) {
|
|
14
23
|
const schema = {
|
|
15
24
|
type: 'object',
|
|
16
|
-
properties: {
|
|
25
|
+
properties: {
|
|
26
|
+
authentic: { type: 'boolean' },
|
|
27
|
+
task: { type: 'boolean' },
|
|
28
|
+
source: structuredClone(schemas.source)
|
|
29
|
+
},
|
|
17
30
|
additionalProperties: true
|
|
18
31
|
}
|
|
19
32
|
|
|
@@ -22,9 +35,8 @@ class Request extends Conditions {
|
|
|
22
35
|
if (definition.input !== undefined) {
|
|
23
36
|
schema.properties.input = definition.input
|
|
24
37
|
required.push('input')
|
|
25
|
-
} else
|
|
38
|
+
} else
|
|
26
39
|
schema.properties.input = { type: 'null' }
|
|
27
|
-
}
|
|
28
40
|
|
|
29
41
|
if (entity === undefined)
|
|
30
42
|
definition.query = false
|
|
@@ -63,5 +75,3 @@ class Request extends Conditions {
|
|
|
63
75
|
return schema
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
|
-
|
|
67
|
-
exports.Request = Request
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { resolve } from 'path'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { yaml } from '@toa.io/generic'
|
|
2
4
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
+
export const query = read(resolve(import.meta.dirname, './query.yaml'))
|
|
6
|
+
export const error = read(resolve(import.meta.dirname, './error.yaml'))
|
|
7
|
+
export const source = read(resolve(import.meta.dirname, './source.yaml'))
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
function read (path) {
|
|
10
|
+
return yaml.load(readFileSync(path, 'utf8'))
|
|
11
|
+
}
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
type: object
|
|
2
2
|
properties:
|
|
3
|
-
id:
|
|
3
|
+
id:
|
|
4
|
+
type: string
|
|
5
|
+
ids:
|
|
6
|
+
type: array
|
|
7
|
+
uniqueItems: true
|
|
8
|
+
minItems: 1
|
|
9
|
+
items:
|
|
10
|
+
type: string
|
|
4
11
|
version:
|
|
5
12
|
type: integer
|
|
6
13
|
minimum: 0
|
|
7
14
|
criteria:
|
|
8
15
|
type: string
|
|
16
|
+
search:
|
|
17
|
+
type: string
|
|
18
|
+
sample:
|
|
19
|
+
type: number
|
|
9
20
|
omit:
|
|
10
21
|
type: integer
|
|
11
22
|
minimum: 0
|
|
@@ -27,4 +38,6 @@ properties:
|
|
|
27
38
|
type: string
|
|
28
39
|
not:
|
|
29
40
|
const: id
|
|
41
|
+
deleted:
|
|
42
|
+
type: boolean
|
|
30
43
|
additionalProperties: false
|
|
@@ -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,9 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { console } from 'openspan'
|
|
2
|
+
import { Connector } from './connector.js'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
const { Connector } = require('./connector')
|
|
5
|
-
|
|
6
|
-
class Discovery extends Connector {
|
|
4
|
+
export class Discovery extends Connector {
|
|
7
5
|
#lookup
|
|
8
6
|
#lookups
|
|
9
7
|
|
|
@@ -25,20 +23,24 @@ class Discovery extends Connector {
|
|
|
25
23
|
this.depends(this.#lookups[id])
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
const since = Date.now()
|
|
27
|
+
|
|
28
|
+
// the wait is unbounded by design, as a dependency may still be starting,
|
|
29
|
+
// so repeating is the only way a component stuck on one stays visible
|
|
30
|
+
const warning = setInterval(() => {
|
|
31
|
+
const waiting = Math.round((Date.now() - since) / 1000)
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
console.warn('Waiting for lookup response', { component: id, waiting })
|
|
34
|
+
}, INTERVAL)
|
|
35
|
+
|
|
36
|
+
warning.unref()
|
|
32
37
|
|
|
33
38
|
const output = await this.#lookups[id].invoke()
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
clearTimeout(timeout)
|
|
40
|
+
clearInterval(warning)
|
|
37
41
|
|
|
38
42
|
return output
|
|
39
43
|
}
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
exports.Discovery = Discovery
|
|
46
|
+
const INTERVAL = 5000
|
package/src/effect.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Observation } from './observation.js'
|
|
2
|
+
|
|
3
|
+
export class Effect extends Observation {
|
|
4
|
+
|
|
5
|
+
async acquire (store) {
|
|
6
|
+
const { query, entity, input } = store.request
|
|
7
|
+
|
|
8
|
+
if (entity === undefined)
|
|
9
|
+
return super.acquire(store)
|
|
10
|
+
|
|
11
|
+
store.scope = await this.scope.ensure(query, entity, input)
|
|
12
|
+
store.state = store.scope.get()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
}
|
package/src/emission.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { Connector } from './connector.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Emission extends Connector {
|
|
3
|
+
export class Emission extends Connector {
|
|
6
4
|
#events
|
|
7
5
|
|
|
8
6
|
constructor (events) {
|
|
@@ -19,5 +17,3 @@ class Emission extends Connector {
|
|
|
19
17
|
await Promise.all(emission)
|
|
20
18
|
}
|
|
21
19
|
}
|
|
22
|
-
|
|
23
|
-
exports.Emission = Emission
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { EntityContractException } from '../exceptions.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
merge,
|
|
5
|
-
overwrite,
|
|
6
|
-
newid
|
|
7
|
-
} = require('@toa.io/generic')
|
|
8
|
-
const { EntityContractException } = require('../exceptions')
|
|
9
|
-
|
|
10
|
-
class Changeset {
|
|
3
|
+
export class Changeset {
|
|
11
4
|
/** @type {toa.core.Query} */
|
|
12
5
|
query
|
|
13
6
|
|
|
@@ -18,7 +11,7 @@ class Changeset {
|
|
|
18
11
|
this.query = query
|
|
19
12
|
|
|
20
13
|
this.#schema = schema
|
|
21
|
-
this.#state =
|
|
14
|
+
this.#state = {}
|
|
22
15
|
}
|
|
23
16
|
|
|
24
17
|
get () {
|
|
@@ -26,10 +19,12 @@ class Changeset {
|
|
|
26
19
|
}
|
|
27
20
|
|
|
28
21
|
set (value) {
|
|
29
|
-
const error = this.#schema.
|
|
22
|
+
const error = this.#schema.match(value)
|
|
30
23
|
|
|
31
|
-
if (error !== null)
|
|
24
|
+
if (error !== null)
|
|
25
|
+
throw new EntityContractException(error, value)
|
|
32
26
|
|
|
27
|
+
delete value._version
|
|
33
28
|
value._updated = Date.now()
|
|
34
29
|
|
|
35
30
|
this.#state = value
|
|
@@ -39,5 +34,3 @@ class Changeset {
|
|
|
39
34
|
return this.#state
|
|
40
35
|
}
|
|
41
36
|
}
|
|
42
|
-
|
|
43
|
-
exports.Changeset = Changeset
|
package/src/entities/entity.js
CHANGED
|
@@ -1,24 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
import { EntityContractException, EntityGuardException } from '../exceptions.js'
|
|
2
|
+
import { newid } from './newid.js'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
newid
|
|
6
|
-
} = require('@toa.io/generic')
|
|
7
|
-
|
|
8
|
-
const { EntityContractException } = require('../exceptions')
|
|
9
|
-
|
|
10
|
-
class Entity {
|
|
4
|
+
export class Entity {
|
|
5
|
+
deleted = false
|
|
11
6
|
#schema
|
|
7
|
+
#guards
|
|
12
8
|
#origin = null
|
|
13
9
|
#state
|
|
10
|
+
#mutable = true
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
/**
|
|
13
|
+
* @param {boolean} [mutable] whether the entity may be modified and committed
|
|
14
|
+
*/
|
|
15
|
+
constructor (schema, argument, guards, mutable = true) {
|
|
16
16
|
this.#schema = schema
|
|
17
|
+
this.#guards = guards
|
|
17
18
|
|
|
18
19
|
if (typeof argument === 'object') {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/*
|
|
21
|
+
* The origin is the pre-image a commit diffs the new state against. An operation
|
|
22
|
+
* that cannot commit has nothing to diff, so it takes the record as it came from
|
|
23
|
+
* the storage instead of paying for a deep copy of every record it read.
|
|
24
|
+
*/
|
|
25
|
+
this.#mutable = mutable
|
|
26
|
+
|
|
27
|
+
if (mutable) {
|
|
28
|
+
this.#set(structuredClone(argument))
|
|
29
|
+
this.#origin = argument
|
|
30
|
+
} else
|
|
31
|
+
this.#set(argument)
|
|
22
32
|
} else {
|
|
23
33
|
const id = argument === undefined ? newid() : argument
|
|
24
34
|
this.#init(id)
|
|
@@ -29,37 +39,75 @@ class Entity {
|
|
|
29
39
|
return this.#state
|
|
30
40
|
}
|
|
31
41
|
|
|
32
|
-
set (value) {
|
|
33
|
-
|
|
42
|
+
set (value, optional = false) {
|
|
43
|
+
if (!this.#mutable)
|
|
44
|
+
throw new Error('Entity acquired by a read-only operation cannot be modified')
|
|
45
|
+
|
|
46
|
+
if (!optional)
|
|
47
|
+
this.#guard(value)
|
|
48
|
+
|
|
49
|
+
const error = optional ? this.#schema.fitOptional(value) : this.#schema.fit(value)
|
|
34
50
|
|
|
35
|
-
if (error !== null)
|
|
51
|
+
if (error !== null)
|
|
52
|
+
throw new EntityContractException(error, value)
|
|
36
53
|
|
|
54
|
+
this.#revive(value)
|
|
37
55
|
this.#set(value)
|
|
38
56
|
}
|
|
39
57
|
|
|
40
|
-
event () {
|
|
58
|
+
event (input = undefined) {
|
|
41
59
|
return {
|
|
42
60
|
origin: this.#origin,
|
|
43
61
|
state: this.#state,
|
|
44
|
-
|
|
62
|
+
trailers: this.#state._trailers,
|
|
63
|
+
input
|
|
45
64
|
}
|
|
46
65
|
}
|
|
47
66
|
|
|
48
67
|
#init (id) {
|
|
49
|
-
const value = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
const value = { id, _version: 0 }
|
|
69
|
+
|
|
70
|
+
this.set(value, true)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#guard (value) {
|
|
74
|
+
if (this.#guards === undefined)
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
for (const guard of this.#guards) {
|
|
78
|
+
const ok = guard.fit(value, this.#origin)
|
|
79
|
+
|
|
80
|
+
if (ok === false)
|
|
81
|
+
throw new EntityGuardException(guard.name, value)
|
|
53
82
|
}
|
|
83
|
+
}
|
|
54
84
|
|
|
55
|
-
|
|
85
|
+
// deletion is only expressed as a new _deleted timestamp,
|
|
86
|
+
// so committing over a tombstone without touching it means revival
|
|
87
|
+
#revive (value) {
|
|
88
|
+
if (this.#origin?._deleted == null || value._deleted !== this.#origin._deleted)
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
value._deleted = null
|
|
92
|
+
this.deleted = false
|
|
56
93
|
}
|
|
57
94
|
|
|
58
95
|
#set (value) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
96
|
+
if (!('_trailers' in value))
|
|
97
|
+
Object.defineProperty(value, '_trailers', {
|
|
98
|
+
writable: false,
|
|
99
|
+
configurable: false,
|
|
100
|
+
enumerable: false,
|
|
101
|
+
value: {}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
if (!('_created' in value)) {
|
|
105
|
+
value._created = Date.now()
|
|
106
|
+
value._updated ??= value._created
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if ('_deleted' in value && value._deleted !== null)
|
|
110
|
+
this.deleted = true
|
|
63
111
|
|
|
64
112
|
if (this.#state !== undefined) {
|
|
65
113
|
value._updated = Date.now()
|
|
@@ -69,5 +117,3 @@ class Entity {
|
|
|
69
117
|
this.#state = value
|
|
70
118
|
}
|
|
71
119
|
}
|
|
72
|
-
|
|
73
|
-
exports.Entity = Entity
|
package/src/entities/factory.js
CHANGED
|
@@ -1,26 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
import { newid } from './newid.js'
|
|
2
|
+
import { Entity } from './entity.js'
|
|
3
|
+
import { EntitySet } from './set.js'
|
|
4
|
+
import { Changeset } from './changeset.js'
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
const { EntitySet } = require('./set')
|
|
5
|
-
const { Changeset } = require('./changeset')
|
|
6
|
-
|
|
7
|
-
class Factory {
|
|
6
|
+
export class Factory {
|
|
8
7
|
#schema
|
|
8
|
+
#guards
|
|
9
9
|
|
|
10
|
-
constructor (schema) {
|
|
10
|
+
constructor (schema, guards) {
|
|
11
11
|
this.#schema = schema
|
|
12
|
+
this.#guards = guards
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fit (values) {
|
|
16
|
+
this.#schema.validate({ id: newid(), ...values }, 'Entity')
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
init (id) {
|
|
15
|
-
return new Entity(this.#schema, id)
|
|
20
|
+
return new Entity(this.#schema, id, this.#guards)
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
object (record) {
|
|
19
|
-
return new Entity(this.#schema, record)
|
|
23
|
+
object (record, mutable = true) {
|
|
24
|
+
return new Entity(this.#schema, record, this.#guards, mutable)
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
objects (recordset) {
|
|
23
|
-
const set = recordset.map((record) => this.object(record))
|
|
27
|
+
objects (recordset, init, mutable = true) {
|
|
28
|
+
const set = recordset.map((record) => this.object(record, mutable))
|
|
29
|
+
|
|
30
|
+
if (init !== undefined)
|
|
31
|
+
for (const id of init)
|
|
32
|
+
set.unshift(this.init(id))
|
|
24
33
|
|
|
25
34
|
return new EntitySet(set)
|
|
26
35
|
}
|
|
@@ -29,5 +38,3 @@ class Factory {
|
|
|
29
38
|
return new Changeset(this.#schema, query)
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
|
-
|
|
33
|
-
exports.Factory = Factory
|
package/src/entities/index.js
CHANGED
package/src/entities/set.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { SystemException } from '../exceptions.js'
|
|
2
2
|
|
|
3
|
-
class EntitySet {
|
|
3
|
+
export class EntitySet {
|
|
4
4
|
#set
|
|
5
5
|
|
|
6
6
|
constructor (set) {
|
|
@@ -10,6 +10,15 @@ class EntitySet {
|
|
|
10
10
|
get () {
|
|
11
11
|
return this.#set.map((entity) => entity.get())
|
|
12
12
|
}
|
|
13
|
-
}
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
set (values) {
|
|
15
|
+
if (values.length !== this.#set.length)
|
|
16
|
+
throw new SystemException('Objects array must not be modified')
|
|
17
|
+
|
|
18
|
+
values.forEach((value, index) => this.#set[index].set(value))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
events (input = undefined) {
|
|
22
|
+
return this.#set.map((entity) => entity.event(input))
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/event.js
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { Connector } = require('./connector')
|
|
1
|
+
import { console, current, encode } from 'openspan'
|
|
2
|
+
import { Connector } from './connector.js'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* @implements {toa.core.Event}
|
|
7
6
|
*/
|
|
8
|
-
class Event extends Connector {
|
|
7
|
+
export class Event extends Connector {
|
|
9
8
|
/** @type {toa.core.bindings.Emitter} */
|
|
10
9
|
#emitter
|
|
11
10
|
#bridge
|
|
12
11
|
#conditioned
|
|
13
12
|
#subjective
|
|
14
13
|
|
|
14
|
+
/** @type {string} */
|
|
15
|
+
#label
|
|
16
|
+
|
|
15
17
|
constructor (definition, emitter, bridge = undefined) {
|
|
16
18
|
super()
|
|
17
19
|
|
|
18
20
|
this.#conditioned = definition.conditioned
|
|
19
21
|
this.#subjective = definition.subjective
|
|
22
|
+
this.#label = definition.label ?? 'event'
|
|
20
23
|
this.#emitter = emitter
|
|
21
24
|
this.#bridge = bridge
|
|
22
25
|
|
|
@@ -32,9 +35,20 @@ class Event extends Connector {
|
|
|
32
35
|
/** @type {toa.core.Message} */
|
|
33
36
|
const message = { payload }
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
const options = {
|
|
39
|
+
name: `${this.#label} publish`,
|
|
40
|
+
kind: 'producer',
|
|
41
|
+
attributes: { 'messaging.destination.name': this.#label }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await console.span(options, async () => {
|
|
45
|
+
const context = current()
|
|
46
|
+
|
|
47
|
+
if (context !== undefined)
|
|
48
|
+
message.telemetry = encode(context)
|
|
49
|
+
|
|
50
|
+
await this.#emitter.emit(message)
|
|
51
|
+
})
|
|
36
52
|
}
|
|
37
53
|
}
|
|
38
54
|
}
|
|
39
|
-
|
|
40
|
-
exports.Event = Event
|