@toa.io/boot 1.0.0-alpha.283 → 1.0.0-alpha.286
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 +8 -8
- package/src/atom.js +11 -7
- package/src/atomicity.js +1 -1
- package/src/bindings/consume.js +5 -2
- package/src/bindings/produce.js +2 -4
- package/src/bridge.js +3 -4
- package/src/cascade.js +13 -3
- package/src/component.js +33 -15
- package/src/composition.js +44 -25
- package/src/contract.js +3 -4
- package/src/emission.js +17 -6
- package/src/extensions/aspects.js +1 -1
- package/src/extensions/component.js +2 -2
- package/src/extensions/context.js +2 -2
- package/src/extensions/emitter.js +5 -4
- package/src/extensions/instances.js +1 -1
- package/src/extensions/load.js +6 -4
- package/src/extensions/manage.js +3 -4
- package/src/extensions/receiver.js +4 -5
- package/src/extensions/resolve.js +2 -2
- package/src/extensions/storage.js +2 -2
- package/src/extensions/tenants.js +1 -1
- package/src/guards.js +7 -6
- package/src/host.js +1 -1
- package/src/index.js +22 -2
- package/src/manifest.js +8 -4
- package/src/manifest.test.js +1 -2
- package/src/operation.js +19 -5
- package/src/outbox.js +2 -2
- package/src/rc.js +1 -1
- package/src/receivers.js +20 -9
- package/src/remote.js +12 -9
- package/src/span.js +2 -1
- package/src/storage.js +12 -3
- package/src/storage.test.js +47 -0
- package/types/bindings.d.ts +6 -2
- package/types/index.d.ts +14 -7
- package/types/manifest.d.ts +4 -3
- package/CHANGELOG.md +0 -188
- package/src/exposition.js +0 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/boot",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.286",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Toa Boot",
|
|
6
6
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@toa.io/atomicity": "1.0.0-alpha.
|
|
25
|
-
"@toa.io/core": "1.0.0-alpha.
|
|
26
|
-
"@toa.io/generic": "1.0.0-alpha.
|
|
27
|
-
"@toa.io/norm": "1.0.0-alpha.
|
|
28
|
-
"@toa.io/schemas": "1.0.0-alpha.
|
|
24
|
+
"@toa.io/atomicity": "1.0.0-alpha.286",
|
|
25
|
+
"@toa.io/core": "1.0.0-alpha.286",
|
|
26
|
+
"@toa.io/generic": "1.0.0-alpha.286",
|
|
27
|
+
"@toa.io/norm": "1.0.0-alpha.286",
|
|
28
|
+
"@toa.io/schemas": "1.0.0-alpha.286",
|
|
29
29
|
"clone-deep": "4.0.1",
|
|
30
30
|
"dotenv": "17.4.2",
|
|
31
|
-
"openspan": "1.0.0-alpha.
|
|
31
|
+
"openspan": "1.0.0-alpha.286"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "81f3d0688f17668b902be336d1749cc715edcdb6"
|
|
34
34
|
}
|
package/src/atom.js
CHANGED
|
@@ -6,14 +6,14 @@ import * as boot from './index.js'
|
|
|
6
6
|
* The atom as a system aspect. Every component has `context.atom`, with nothing declared in its
|
|
7
7
|
* manifest and nothing of its own to configure.
|
|
8
8
|
*
|
|
9
|
-
* @implements {toa.core.extensions.Aspect}
|
|
9
|
+
* @implements {import('@toa.io/core/types').extensions.Aspect}
|
|
10
10
|
*/
|
|
11
11
|
class Aspect extends Connector {
|
|
12
12
|
name = 'atom'
|
|
13
13
|
|
|
14
14
|
#atom
|
|
15
15
|
|
|
16
|
-
constructor
|
|
16
|
+
constructor(atom) {
|
|
17
17
|
super()
|
|
18
18
|
|
|
19
19
|
this.#atom = atom
|
|
@@ -23,12 +23,16 @@ class Aspect extends Connector {
|
|
|
23
23
|
|
|
24
24
|
// the methods are named rather than forwarded, so that the connector's lifecycle is not
|
|
25
25
|
// reachable from an algorithm
|
|
26
|
-
invoke
|
|
26
|
+
invoke(method, ...args) {
|
|
27
27
|
switch (method) {
|
|
28
|
-
case 'slots':
|
|
29
|
-
|
|
30
|
-
case '
|
|
31
|
-
|
|
28
|
+
case 'slots':
|
|
29
|
+
return this.#atom.slots(...args)
|
|
30
|
+
case 'meter':
|
|
31
|
+
return this.#atom.meter(...args)
|
|
32
|
+
case 'lock':
|
|
33
|
+
return this.#atom.lock(...args)
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(`Atom aspect has no '${method}' method`)
|
|
32
36
|
}
|
|
33
37
|
}
|
|
34
38
|
}
|
package/src/atomicity.js
CHANGED
|
@@ -6,6 +6,6 @@ import { Factory } from '@toa.io/atomicity'
|
|
|
6
6
|
* see the connector's readme.
|
|
7
7
|
*
|
|
8
8
|
* @param {string} group
|
|
9
|
-
* @returns {toa.core.atomicity.Atom}
|
|
9
|
+
* @returns {import('@toa.io/core/types').atomicity.Atom}
|
|
10
10
|
*/
|
|
11
11
|
export const atomicity = (group) => new Factory().atom(group)
|
package/src/bindings/consume.js
CHANGED
|
@@ -2,5 +2,8 @@ import { LOOP } from './constants.js'
|
|
|
2
2
|
import { factory } from './factory.js'
|
|
3
3
|
|
|
4
4
|
export const consume = async (locator, endpoint, bindings) =>
|
|
5
|
-
await Promise.all(
|
|
6
|
-
|
|
5
|
+
await Promise.all(
|
|
6
|
+
[LOOP]
|
|
7
|
+
.concat(bindings)
|
|
8
|
+
.map(async (binding) => (await factory(binding)).consumer(locator, endpoint))
|
|
9
|
+
)
|
package/src/bindings/produce.js
CHANGED
|
@@ -10,10 +10,8 @@ export const produce = async (component, operations) => {
|
|
|
10
10
|
const producer = made.producer(component.locator, endpoints, component)
|
|
11
11
|
const { properties } = await import(binding)
|
|
12
12
|
|
|
13
|
-
if (properties.local === true)
|
|
14
|
-
|
|
15
|
-
else
|
|
16
|
-
other.push(producer)
|
|
13
|
+
if (properties.local === true) local.push(producer)
|
|
14
|
+
else other.push(producer)
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
return { local, other }
|
package/src/bridge.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export async function algorithm
|
|
1
|
+
export async function algorithm(bridge, path, endpoint, context) {
|
|
2
2
|
const factory = await resolve(bridge)
|
|
3
3
|
const algorithm = await factory.algorithm(path, endpoint, context)
|
|
4
4
|
|
|
@@ -16,11 +16,10 @@ export const receiver = async (bridge, path, label) =>
|
|
|
16
16
|
export const guard = async (bridge, path, label, context) =>
|
|
17
17
|
(await resolve(bridge)).guard(path, label, context)
|
|
18
18
|
|
|
19
|
-
export async function rc
|
|
19
|
+
export async function rc(bridge, path, context) {
|
|
20
20
|
const factory = await resolve(bridge)
|
|
21
21
|
|
|
22
|
-
if (factory.rc === undefined)
|
|
23
|
-
return
|
|
22
|
+
if (factory.rc === undefined) return
|
|
24
23
|
|
|
25
24
|
return factory.rc(path, context)
|
|
26
25
|
}
|
package/src/cascade.js
CHANGED
|
@@ -2,13 +2,18 @@ import { Cascade } from '@toa.io/core'
|
|
|
2
2
|
|
|
3
3
|
import * as boot from './index.js'
|
|
4
4
|
|
|
5
|
-
export async function cascade
|
|
5
|
+
export async function cascade(manifest, endpoint, definition, context, preflight) {
|
|
6
6
|
const bridges = []
|
|
7
7
|
|
|
8
8
|
if (definition.forward) endpoint = definition.forward
|
|
9
9
|
|
|
10
10
|
if (definition.bridge) {
|
|
11
|
-
const bridge = await boot.bridge.algorithm(
|
|
11
|
+
const bridge = await boot.bridge.algorithm(
|
|
12
|
+
definition.bridge,
|
|
13
|
+
manifest.path,
|
|
14
|
+
endpoint,
|
|
15
|
+
context
|
|
16
|
+
)
|
|
12
17
|
|
|
13
18
|
bridges.unshift(bridge)
|
|
14
19
|
}
|
|
@@ -20,7 +25,12 @@ export async function cascade (manifest, endpoint, definition, context, prefligh
|
|
|
20
25
|
|
|
21
26
|
if (operation === undefined) continue
|
|
22
27
|
|
|
23
|
-
const bridge = await boot.bridge.algorithm(
|
|
28
|
+
const bridge = await boot.bridge.algorithm(
|
|
29
|
+
operation.bridge,
|
|
30
|
+
prototype.path,
|
|
31
|
+
endpoint,
|
|
32
|
+
context
|
|
33
|
+
)
|
|
24
34
|
|
|
25
35
|
bridges.unshift(bridge)
|
|
26
36
|
}
|
package/src/component.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Component, Locator, State, entities } from '@toa.io/core'
|
|
2
|
-
import
|
|
2
|
+
import { entity as declaration } from '@toa.io/norm'
|
|
3
|
+
import { schema as compileSchema } from '@toa.io/schemas'
|
|
3
4
|
|
|
4
5
|
import * as boot from './index.js'
|
|
5
6
|
import { span } from './span.js'
|
|
@@ -23,11 +24,23 @@ const create = async (manifest, locator) => {
|
|
|
23
24
|
let state
|
|
24
25
|
|
|
25
26
|
if (manifest.entity !== undefined) {
|
|
26
|
-
const
|
|
27
|
+
const schemas = compile(manifest.entity)
|
|
27
28
|
const guards = await boot.guards(manifest, context)
|
|
28
|
-
const
|
|
29
|
+
const blank = manifest.entity.blank ?? {}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
// fitted here rather than where the first record is written, so a component that
|
|
32
|
+
// declares a blank nothing can hold does not boot
|
|
33
|
+
const error = schemas.changeset.fit(blank)
|
|
34
|
+
|
|
35
|
+
if (error !== null)
|
|
36
|
+
throw new Error(`Component '${locator.id}' entity blank: ${error.message}`)
|
|
37
|
+
|
|
38
|
+
state = new State(
|
|
39
|
+
storage,
|
|
40
|
+
new entities.Factory(schemas, blank, guards),
|
|
41
|
+
outbox,
|
|
42
|
+
manifest.entity.associated
|
|
43
|
+
)
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
const phases = await boot.rc(manifest, context)
|
|
@@ -42,31 +55,36 @@ const create = async (manifest, locator) => {
|
|
|
42
55
|
|
|
43
56
|
const decorated = boot.extensions.component(component)
|
|
44
57
|
|
|
45
|
-
if (phases?.settle !== undefined)
|
|
46
|
-
decorated.settle = phases.settle
|
|
58
|
+
if (phases?.settle !== undefined) decorated.settle = phases.settle
|
|
47
59
|
|
|
48
60
|
// a dependency closes after its dependant, so the component is already closed
|
|
49
61
|
// when the RC releases what it opened
|
|
50
|
-
if (phases?.dispose !== undefined)
|
|
51
|
-
decorated.depends(phases.dispose)
|
|
62
|
+
if (phases?.dispose !== undefined) decorated.depends(phases.dispose)
|
|
52
63
|
|
|
53
64
|
return decorated
|
|
54
65
|
}
|
|
55
66
|
|
|
56
|
-
async function bootOperations
|
|
57
|
-
if (manifest.operations === undefined)
|
|
58
|
-
return {}
|
|
67
|
+
async function bootOperations(manifest, context, state, preflight) {
|
|
68
|
+
if (manifest.operations === undefined) return {}
|
|
59
69
|
|
|
60
70
|
const entries = Object.entries(manifest.operations)
|
|
61
71
|
|
|
62
72
|
// each one loads its algorithm from disk and compiles its contracts
|
|
63
|
-
const booted = await Promise.all(
|
|
64
|
-
|
|
73
|
+
const booted = await Promise.all(
|
|
74
|
+
entries.map(([endpoint, definition]) =>
|
|
75
|
+
boot.operation(manifest, endpoint, definition, context, state, preflight)
|
|
76
|
+
)
|
|
77
|
+
)
|
|
65
78
|
|
|
66
79
|
const operations = {}
|
|
67
80
|
|
|
68
|
-
for (let i = 0; i < entries.length; i++)
|
|
69
|
-
operations[entries[i][0]] = booted[i]
|
|
81
|
+
for (let i = 0; i < entries.length; i++) operations[entries[i][0]] = booted[i]
|
|
70
82
|
|
|
71
83
|
return operations
|
|
72
84
|
}
|
|
85
|
+
|
|
86
|
+
/** What a stored record must fit, and what a changeset may. */
|
|
87
|
+
const compile = (entity) => ({
|
|
88
|
+
entity: compileSchema(declaration.schema(entity)),
|
|
89
|
+
changeset: compileSchema(declaration.changeset(entity))
|
|
90
|
+
})
|
package/src/composition.js
CHANGED
|
@@ -5,34 +5,52 @@ import { version } from '@toa.io/runtime'
|
|
|
5
5
|
import * as boot from './index.js'
|
|
6
6
|
import { span } from './span.js'
|
|
7
7
|
|
|
8
|
-
export async function composition
|
|
8
|
+
export async function composition(paths, options) {
|
|
9
9
|
options = Object.assign({}, options)
|
|
10
10
|
|
|
11
11
|
return span('boot composition', async () => {
|
|
12
|
-
const manifests = await span(
|
|
13
|
-
|
|
12
|
+
const manifests = await span(
|
|
13
|
+
'load manifests',
|
|
14
|
+
async () => await Promise.all(paths.map((path) => boot.manifest(path, options)))
|
|
15
|
+
)
|
|
14
16
|
|
|
15
17
|
console.info('Starting composition', {
|
|
16
18
|
runtime: version,
|
|
17
19
|
components: manifests.map((manifest) => manifest.locator.id)
|
|
18
20
|
})
|
|
19
21
|
|
|
20
|
-
const tenants = await span(
|
|
21
|
-
|
|
22
|
+
const tenants = await span(
|
|
23
|
+
'create tenants',
|
|
24
|
+
async () => await Promise.all(manifests.map(boot.extensions.tenants))
|
|
25
|
+
)
|
|
22
26
|
|
|
23
|
-
const expositions = await span(
|
|
24
|
-
|
|
27
|
+
const expositions = await span(
|
|
28
|
+
'expose discovery',
|
|
29
|
+
async () => await Promise.all(manifests.map(boot.discovery.expose))
|
|
30
|
+
)
|
|
25
31
|
|
|
26
32
|
try {
|
|
27
|
-
const components = await span(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
const components = await span(
|
|
34
|
+
'create components',
|
|
35
|
+
async () => await Promise.all(manifests.map(boot.component))
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const groups = await Promise.all(
|
|
39
|
+
components.map(
|
|
40
|
+
async (component, index) =>
|
|
41
|
+
await boot.bindings.produce(component, manifests[index].operations)
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const receivers = await span(
|
|
46
|
+
'create receivers',
|
|
47
|
+
async () =>
|
|
48
|
+
await Promise.all(
|
|
49
|
+
components.map((component, index) =>
|
|
50
|
+
boot.receivers(manifests[index], component)
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
)
|
|
36
54
|
|
|
37
55
|
const producers = []
|
|
38
56
|
const settles = []
|
|
@@ -55,22 +73,23 @@ export async function composition (paths, options) {
|
|
|
55
73
|
for (const receiver of receivers[i])
|
|
56
74
|
for (const producer of serving) receiver.depends(producer)
|
|
57
75
|
|
|
58
|
-
if (settle === undefined)
|
|
59
|
-
continue
|
|
76
|
+
if (settle === undefined) continue
|
|
60
77
|
|
|
61
|
-
if (local.length > 0)
|
|
62
|
-
settle.depends(local)
|
|
78
|
+
if (local.length > 0) settle.depends(local)
|
|
63
79
|
|
|
64
|
-
for (const producer of other)
|
|
65
|
-
producer.depends(settle)
|
|
80
|
+
for (const producer of other) producer.depends(settle)
|
|
66
81
|
|
|
67
|
-
for (const receiver of receivers[i])
|
|
68
|
-
receiver.depends(settle)
|
|
82
|
+
for (const receiver of receivers[i]) receiver.depends(settle)
|
|
69
83
|
|
|
70
84
|
settles.push(settle)
|
|
71
85
|
}
|
|
72
86
|
|
|
73
|
-
const composition = new Composition(
|
|
87
|
+
const composition = new Composition(
|
|
88
|
+
expositions.flat(),
|
|
89
|
+
producers.concat(settles),
|
|
90
|
+
receivers.flat(),
|
|
91
|
+
tenants.flat()
|
|
92
|
+
)
|
|
74
93
|
|
|
75
94
|
/*
|
|
76
95
|
* A lookup that is never answered holds a connection open, and whoever
|
package/src/contract.js
CHANGED
|
@@ -5,7 +5,7 @@ const { Request, Reply } = contract
|
|
|
5
5
|
|
|
6
6
|
export const request = (definition, entity) => {
|
|
7
7
|
const request = Request.schema(definition, entity)
|
|
8
|
-
const schema = schemas.schema(request
|
|
8
|
+
const schema = schemas.schema(request)
|
|
9
9
|
|
|
10
10
|
return new Request(schema, definition)
|
|
11
11
|
}
|
|
@@ -13,9 +13,8 @@ export const request = (definition, entity) => {
|
|
|
13
13
|
export const reply = (output, errors) => {
|
|
14
14
|
const reply = Reply.schema(output, errors)
|
|
15
15
|
|
|
16
|
-
// a reply is read, not shaped:
|
|
17
|
-
|
|
18
|
-
const schema = schemas.schema(reply, { coerceTypes: false, useDefaults: false })
|
|
16
|
+
// a reply is read, not shaped: a value that only fits once coerced does not fit
|
|
17
|
+
const schema = schemas.schema(reply, { coerceTypes: false })
|
|
19
18
|
|
|
20
19
|
return new Reply(schema)
|
|
21
20
|
}
|
package/src/emission.js
CHANGED
|
@@ -6,13 +6,24 @@ import * as extensions from './extensions/index.js'
|
|
|
6
6
|
export const emission = async (definitions, locator, context) => {
|
|
7
7
|
if (definitions === undefined) return
|
|
8
8
|
|
|
9
|
-
const events = await Promise.all(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const events = await Promise.all(
|
|
10
|
+
Object.entries(definitions).map(async ([label, definition]) => {
|
|
11
|
+
const emitter = await boot.bindings.emit(definition.binding, locator, label)
|
|
12
|
+
const decorator = extensions.emitter(emitter, label, locator)
|
|
13
|
+
const bridge = await boot.bridge.event(
|
|
14
|
+
definition.bridge,
|
|
15
|
+
definition.path,
|
|
16
|
+
label,
|
|
17
|
+
context
|
|
18
|
+
)
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
return new Event(
|
|
21
|
+
{ ...definition, label: `${locator.id}.${label}` },
|
|
22
|
+
decorator,
|
|
23
|
+
bridge
|
|
24
|
+
)
|
|
25
|
+
})
|
|
26
|
+
)
|
|
16
27
|
|
|
17
28
|
return new Emission(events)
|
|
18
29
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { instances } from './instances.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @param {toa.core.Component} component
|
|
5
|
-
* @returns {toa.core.Component}
|
|
4
|
+
* @param {import('@toa.io/core').Component} component
|
|
5
|
+
* @returns {import('@toa.io/core').Component}
|
|
6
6
|
*/
|
|
7
7
|
export const component = (component) => {
|
|
8
8
|
let decorated = component
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { instances } from './instances.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @param {toa.core.Context} component
|
|
5
|
-
* @returns {toa.core.Context}
|
|
4
|
+
* @param {import('@toa.io/core').Context} component
|
|
5
|
+
* @returns {import('@toa.io/core').Context}
|
|
6
6
|
*/
|
|
7
7
|
export const context = (component) => {
|
|
8
8
|
let decorated = component
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { instances } from './instances.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @param {toa.core.bindings.Emitter} emitter
|
|
4
|
+
* @param {import('@toa.io/core/types').bindings.Emitter} emitter
|
|
5
5
|
* @param {string} label
|
|
6
|
-
* @param {toa.core.Locator} locator
|
|
7
|
-
* @returns {toa.core.bindings.Emitter}
|
|
6
|
+
* @param {import('@toa.io/core').Locator} locator
|
|
7
|
+
* @returns {import('@toa.io/core/types').bindings.Emitter}
|
|
8
8
|
*/
|
|
9
9
|
export const emitter = (emitter, label, locator) => {
|
|
10
10
|
let decorated = emitter
|
|
11
11
|
|
|
12
12
|
for (const factory of Object.values(instances)) {
|
|
13
|
-
if (factory.emitter !== undefined)
|
|
13
|
+
if (factory.emitter !== undefined)
|
|
14
|
+
decorated = factory.emitter(decorated, label, locator)
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
return decorated
|
package/src/extensions/load.js
CHANGED
|
@@ -4,9 +4,11 @@ import { resolve } from './resolve.js'
|
|
|
4
4
|
* @param {toa.norm.Component} manifest
|
|
5
5
|
*/
|
|
6
6
|
export const load = async (manifest) => {
|
|
7
|
-
if (manifest.extensions === undefined)
|
|
8
|
-
return
|
|
7
|
+
if (manifest.extensions === undefined) return
|
|
9
8
|
|
|
10
|
-
await Promise.all(
|
|
11
|
-
.
|
|
9
|
+
await Promise.all(
|
|
10
|
+
Object.keys(manifest.extensions).map(
|
|
11
|
+
async (name) => await resolve(name, manifest.path)
|
|
12
|
+
)
|
|
13
|
+
)
|
|
12
14
|
}
|
package/src/extensions/manage.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { instances } from './instances.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @param {toa.core.Connector} composition
|
|
5
|
-
* @returns {toa.core.Connector}
|
|
4
|
+
* @param {import('@toa.io/core').Connector} composition
|
|
5
|
+
* @returns {import('@toa.io/core').Connector}
|
|
6
6
|
*/
|
|
7
7
|
export const manage = (composition) => {
|
|
8
8
|
let managed = composition
|
|
9
9
|
|
|
10
10
|
for (const factory of Object.values(instances)) {
|
|
11
|
-
if (factory.manage !== undefined)
|
|
12
|
-
managed = factory.manage(managed)
|
|
11
|
+
if (factory.manage !== undefined) managed = factory.manage(managed)
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
return managed
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { instances } from './instances.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @param {toa.core.Receiver} receiver
|
|
5
|
-
* @param {toa.core.Locator} locator
|
|
6
|
-
* @returns {toa.core.Receiver}
|
|
4
|
+
* @param {import('@toa.io/core/types').Receiver} receiver
|
|
5
|
+
* @param {import('@toa.io/core').Locator} locator
|
|
6
|
+
* @returns {import('@toa.io/core/types').Receiver}
|
|
7
7
|
*/
|
|
8
8
|
export const receiver = (receiver, locator) => {
|
|
9
9
|
let decorated = receiver
|
|
10
10
|
|
|
11
11
|
for (const factory of Object.values(instances)) {
|
|
12
|
-
if (factory.receiver !== undefined)
|
|
13
|
-
decorated = factory.receiver(decorated, locator)
|
|
12
|
+
if (factory.receiver !== undefined) decorated = factory.receiver(decorated, locator)
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
return decorated
|
|
@@ -17,7 +17,7 @@ const loading = {}
|
|
|
17
17
|
*
|
|
18
18
|
* @param {string} reference
|
|
19
19
|
* @param {string} base
|
|
20
|
-
* @returns {Promise<toa.core.extensions.Factory>}
|
|
20
|
+
* @returns {Promise<import('@toa.io/core/types').extensions.Factory>}
|
|
21
21
|
*/
|
|
22
22
|
export const resolve = async (reference, base = process.cwd()) => {
|
|
23
23
|
const path = find(reference, base)
|
|
@@ -35,7 +35,7 @@ export const resolve = async (reference, base = process.cwd()) => {
|
|
|
35
35
|
*
|
|
36
36
|
* @param {string} reference
|
|
37
37
|
* @param {string} base
|
|
38
|
-
* @returns {toa.core.extensions.Factory}
|
|
38
|
+
* @returns {import('@toa.io/core/types').extensions.Factory}
|
|
39
39
|
*/
|
|
40
40
|
export const instance = (reference, base = process.cwd()) => {
|
|
41
41
|
const factory = instances[find(reference, base)]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { instances } from './instances.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @param {toa.core.Storage} storage
|
|
5
|
-
* @returns {toa.core.Storage}
|
|
4
|
+
* @param {import('@toa.io/core/types').storages.Storage} storage
|
|
5
|
+
* @returns {import('@toa.io/core/types').storages.Storage}
|
|
6
6
|
*/
|
|
7
7
|
export const storage = (storage) => {
|
|
8
8
|
let decorated = storage
|
package/src/guards.js
CHANGED
|
@@ -2,16 +2,17 @@ import { Guard } from '@toa.io/core'
|
|
|
2
2
|
import * as boot from './index.js'
|
|
3
3
|
|
|
4
4
|
export async function guards(manifest, context) {
|
|
5
|
-
if (manifest.guards === undefined)
|
|
6
|
-
return
|
|
5
|
+
if (manifest.guards === undefined) return
|
|
7
6
|
|
|
8
7
|
const entries = Object.entries(manifest.guards)
|
|
9
8
|
|
|
10
|
-
return await Promise.all(
|
|
11
|
-
|
|
9
|
+
return await Promise.all(
|
|
10
|
+
entries.map(async ([name, guard]) => {
|
|
11
|
+
const bridge = await load(guard, name, context)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
return new Guard(name, bridge)
|
|
14
|
+
})
|
|
15
|
+
)
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
async function load(guard, name, context) {
|
package/src/host.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as boot from './index.js'
|
|
|
4
4
|
* What the process hosting an extension provides to it. The counterpart of a component's
|
|
5
5
|
* context: an extension reaches the core through this and through nothing else.
|
|
6
6
|
*
|
|
7
|
-
* @returns {toa.core.extensions.Host}
|
|
7
|
+
* @returns {import('@toa.io/core/types').extensions.Host}
|
|
8
8
|
*/
|
|
9
9
|
export const host = () => ({
|
|
10
10
|
remote: boot.remote,
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// the environment is read at module scope elsewhere, so it is loaded first
|
|
2
|
+
// oxlint-disable-next-line import/no-unassigned-import
|
|
2
3
|
import './env.js'
|
|
3
4
|
|
|
4
5
|
import * as bindings from './bindings/index.js'
|
|
@@ -17,7 +18,6 @@ import { events } from './events.js'
|
|
|
17
18
|
import { outbox } from './outbox.js'
|
|
18
19
|
import { atomicity } from './atomicity.js'
|
|
19
20
|
import { atom } from './atom.js'
|
|
20
|
-
import { exposition } from './exposition.js'
|
|
21
21
|
import { manifest } from './manifest.js'
|
|
22
22
|
import { operation } from './operation.js'
|
|
23
23
|
import { receivers, receive } from './receivers.js'
|
|
@@ -27,6 +27,26 @@ import { guards } from './guards.js'
|
|
|
27
27
|
import { host } from './host.js'
|
|
28
28
|
import { rc } from './rc.js'
|
|
29
29
|
|
|
30
|
-
export {
|
|
30
|
+
export {
|
|
31
|
+
call,
|
|
32
|
+
cascade,
|
|
33
|
+
component,
|
|
34
|
+
composition,
|
|
35
|
+
context,
|
|
36
|
+
emission,
|
|
37
|
+
events,
|
|
38
|
+
outbox,
|
|
39
|
+
atomicity,
|
|
40
|
+
atom,
|
|
41
|
+
host,
|
|
42
|
+
manifest,
|
|
43
|
+
operation,
|
|
44
|
+
receivers,
|
|
45
|
+
receive,
|
|
46
|
+
remote,
|
|
47
|
+
storage,
|
|
48
|
+
guards,
|
|
49
|
+
rc
|
|
50
|
+
}
|
|
31
51
|
|
|
32
52
|
export { bindings, bridge, contract, discovery, extensions }
|
package/src/manifest.js
CHANGED
|
@@ -9,8 +9,10 @@ import { span } from './span.js'
|
|
|
9
9
|
export const manifest = async (path, options = {}) => {
|
|
10
10
|
options = merge(clone(options), DEFAULTS)
|
|
11
11
|
|
|
12
|
-
const manifest = await span(
|
|
13
|
-
|
|
12
|
+
const manifest = await span(
|
|
13
|
+
{ name: `manifest ${basename(path)}`, attributes: { path } },
|
|
14
|
+
async () => await load(path)
|
|
15
|
+
)
|
|
14
16
|
|
|
15
17
|
if (options?.bindings !== undefined) {
|
|
16
18
|
if ('operations' in manifest) {
|
|
@@ -30,7 +32,8 @@ export const manifest = async (path, options = {}) => {
|
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
if (asyncBinding === undefined)
|
|
35
|
+
if (asyncBinding === undefined)
|
|
36
|
+
throw new Error('Bindings override must contain at least one async binding')
|
|
34
37
|
|
|
35
38
|
if ('events' in manifest) {
|
|
36
39
|
for (const event of Object.values(manifest.events)) event.binding = asyncBinding
|
|
@@ -51,7 +54,8 @@ export const manifest = async (path, options = {}) => {
|
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
if ('storage' in options && 'entity' in manifest)
|
|
57
|
+
if ('storage' in options && 'entity' in manifest)
|
|
58
|
+
manifest.entity.storage = options.storage
|
|
55
59
|
|
|
56
60
|
manifest.locator = new Locator(manifest.name, manifest.namespace)
|
|
57
61
|
|
package/src/manifest.test.js
CHANGED
|
@@ -7,7 +7,6 @@ mock.module('@toa.io/norm', { namedExports: { component: () => mockComponent() }
|
|
|
7
7
|
|
|
8
8
|
const { manifest } = await import('./manifest.js')
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
const path = generate()
|
|
12
11
|
|
|
13
12
|
it('should not modify options', async () => {
|
|
@@ -18,6 +17,6 @@ it('should not modify options', async () => {
|
|
|
18
17
|
assert.deepStrictEqual(options.extensions.length, 2)
|
|
19
18
|
})
|
|
20
19
|
|
|
21
|
-
function mockComponent
|
|
20
|
+
function mockComponent() {
|
|
22
21
|
return { name: generate(), namespace: generate() }
|
|
23
22
|
}
|
package/src/operation.js
CHANGED
|
@@ -1,17 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Transition,
|
|
3
|
+
Observation,
|
|
4
|
+
Assignment,
|
|
5
|
+
Operation,
|
|
6
|
+
Query,
|
|
7
|
+
Effect,
|
|
8
|
+
Unmanaged
|
|
9
|
+
} from '@toa.io/core'
|
|
2
10
|
|
|
3
11
|
import * as boot from './index.js'
|
|
4
12
|
|
|
5
|
-
export async function operation
|
|
13
|
+
export async function operation(
|
|
14
|
+
manifest,
|
|
15
|
+
endpoint,
|
|
16
|
+
definition,
|
|
17
|
+
context,
|
|
18
|
+
scope,
|
|
19
|
+
preflight
|
|
20
|
+
) {
|
|
6
21
|
const cascade = await boot.cascade(manifest, endpoint, definition, context, preflight)
|
|
7
22
|
const reply = boot.contract.reply(definition.output, definition.errors)
|
|
8
23
|
const input = definition.input
|
|
9
24
|
const request = boot.contract.request({ input }, manifest.entity)
|
|
10
25
|
const contracts = { reply, request }
|
|
11
26
|
|
|
12
|
-
const query =
|
|
13
|
-
? undefined
|
|
14
|
-
: new Query(manifest.entity.schema.properties)
|
|
27
|
+
const query =
|
|
28
|
+
manifest.entity === undefined ? undefined : new Query(manifest.entity.properties)
|
|
15
29
|
|
|
16
30
|
const Type = TYPES[definition.type]
|
|
17
31
|
|
package/src/outbox.js
CHANGED
|
@@ -7,8 +7,8 @@ import * as boot from './index.js'
|
|
|
7
7
|
* static check that keeps the common case free of a transaction.
|
|
8
8
|
*
|
|
9
9
|
* @param {toa.norm.Component} manifest
|
|
10
|
-
* @param {toa.core.Storage} [storage]
|
|
11
|
-
* @param {toa.core.Emission} [emission]
|
|
10
|
+
* @param {import('@toa.io/core/types').storages.Storage} [storage]
|
|
11
|
+
* @param {import('@toa.io/core').Emission} [emission]
|
|
12
12
|
*/
|
|
13
13
|
export const outbox = (manifest, storage, emission) => {
|
|
14
14
|
if (emission === undefined) return
|
package/src/rc.js
CHANGED
package/src/receivers.js
CHANGED
|
@@ -18,14 +18,25 @@ export const receivers = async (manifest, component) => {
|
|
|
18
18
|
// the origin of the calls this receiver makes to its local operation
|
|
19
19
|
const origin = { namespace: source.namespace, component: source.name, event }
|
|
20
20
|
|
|
21
|
-
const bridge =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const bridge =
|
|
22
|
+
definition.bridge !== undefined
|
|
23
|
+
? await boot.bridge.receiver(definition.bridge, manifest.path, label)
|
|
24
|
+
: undefined
|
|
25
|
+
const receiver = new Receiver(
|
|
26
|
+
{ ...definition, label, destination, origin },
|
|
27
|
+
local,
|
|
28
|
+
bridge
|
|
29
|
+
)
|
|
25
30
|
const decorator = extensions.receiver(receiver, manifest.locator)
|
|
26
31
|
|
|
27
|
-
const transport = definition.binding ?? await resolveBinding(locator, label)
|
|
28
|
-
const binding = await boot.bindings.receive(
|
|
32
|
+
const transport = definition.binding ?? (await resolveBinding(locator, label))
|
|
33
|
+
const binding = await boot.bindings.receive(
|
|
34
|
+
transport,
|
|
35
|
+
source,
|
|
36
|
+
label,
|
|
37
|
+
manifest.locator.id,
|
|
38
|
+
decorator
|
|
39
|
+
)
|
|
29
40
|
|
|
30
41
|
binding.depends(component)
|
|
31
42
|
receivers.push(binding)
|
|
@@ -34,7 +45,7 @@ export const receivers = async (manifest, component) => {
|
|
|
34
45
|
return receivers
|
|
35
46
|
}
|
|
36
47
|
|
|
37
|
-
export async function receive
|
|
48
|
+
export async function receive(label, group, callback) {
|
|
38
49
|
if (callback === undefined) {
|
|
39
50
|
callback = group
|
|
40
51
|
group = undefined
|
|
@@ -47,11 +58,11 @@ export async function receive (label, group, callback) {
|
|
|
47
58
|
}
|
|
48
59
|
|
|
49
60
|
/**
|
|
50
|
-
* @param {toa.core.Locator} locator
|
|
61
|
+
* @param {import('@toa.io/core').Locator} locator
|
|
51
62
|
* @param {string} label
|
|
52
63
|
* @return {Promise<string>}
|
|
53
64
|
*/
|
|
54
|
-
async function resolveBinding
|
|
65
|
+
async function resolveBinding(locator, label) {
|
|
55
66
|
const event = label.split('.').pop()
|
|
56
67
|
const discovery = await boot.discovery.discovery()
|
|
57
68
|
const { events } = await discovery.lookup(locator)
|
package/src/remote.js
CHANGED
|
@@ -5,8 +5,8 @@ import { remap } from '@toa.io/generic'
|
|
|
5
5
|
import * as boot from './index.js'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @param {toa.core.Locator} locator
|
|
9
|
-
* @param {toa.core.Source} [source] the origin stamped on every call made through this remote
|
|
8
|
+
* @param {import('@toa.io/core').Locator} locator
|
|
9
|
+
* @param {import('@toa.io/core/types').Source} [source] the origin stamped on every call made through this remote
|
|
10
10
|
* @param {toa.norm.Component} [manifest] skips discovery when the manifest is already known
|
|
11
11
|
*/
|
|
12
12
|
export const remote = async (locator, source, manifest) => {
|
|
@@ -20,22 +20,25 @@ export const remote = async (locator, source, manifest) => {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// a call binds its consumers, which are loaded rather than required
|
|
23
|
-
const calls =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const calls =
|
|
24
|
+
manifest.operations === undefined
|
|
25
|
+
? {}
|
|
26
|
+
: await settle(
|
|
27
|
+
remap(manifest.operations, (definition, endpoint) =>
|
|
28
|
+
boot.call(locator, endpoint, definition, manifest.entity, source)
|
|
29
|
+
)
|
|
30
|
+
)
|
|
27
31
|
|
|
28
32
|
const remote = new Remote(locator, calls)
|
|
29
33
|
|
|
30
34
|
// ensure discovery shutdown
|
|
31
|
-
if (discovery !== undefined)
|
|
32
|
-
remote.depends(discovery)
|
|
35
|
+
if (discovery !== undefined) remote.depends(discovery)
|
|
33
36
|
|
|
34
37
|
return remote
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
/** An object whose values are promises, as an object of what they resolve to. */
|
|
38
|
-
async function settle
|
|
41
|
+
async function settle(object) {
|
|
39
42
|
const entries = Object.entries(object)
|
|
40
43
|
const values = await Promise.all(entries.map(([, promise]) => promise))
|
|
41
44
|
|
package/src/span.js
CHANGED
|
@@ -14,4 +14,5 @@ if (enabled) traces({ exporters: { console: {} } })
|
|
|
14
14
|
* @param {() => Promise<any>} task
|
|
15
15
|
* @returns {Promise<any>}
|
|
16
16
|
*/
|
|
17
|
-
export const span = async (options, task) =>
|
|
17
|
+
export const span = async (options, task) =>
|
|
18
|
+
enabled ? console.span(options, task) : task()
|
package/src/storage.js
CHANGED
|
@@ -16,16 +16,25 @@ export const storage = async (manifest, outbox) => {
|
|
|
16
16
|
|
|
17
17
|
const Factory = await load(manifest)
|
|
18
18
|
|
|
19
|
-
/** @type {toa.core.storages.Factory} */
|
|
19
|
+
/** @type {import('@toa.io/core/types').storages.Factory} */
|
|
20
20
|
const factory = new Factory()
|
|
21
21
|
const storage = factory.storage(manifest.locator, manifest.entity, { outbox })
|
|
22
22
|
|
|
23
|
+
// a component whose structure nothing will make must not start with the structure it lacks
|
|
24
|
+
if (manifest.entity.migrations?.length > 0 && storage.migrates !== true)
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Component '${manifest.locator.id}' declares migrations, ` +
|
|
27
|
+
`which storage '${manifest.entity.storage}' does not apply`
|
|
28
|
+
)
|
|
29
|
+
|
|
23
30
|
return extensions.storage(storage)
|
|
24
31
|
}
|
|
25
32
|
|
|
26
|
-
async function load
|
|
33
|
+
async function load(component) {
|
|
27
34
|
const reference = component.entity.storage
|
|
28
|
-
const path = require.resolve(reference, {
|
|
35
|
+
const path = require.resolve(reference, {
|
|
36
|
+
paths: [component.path, import.meta.dirname]
|
|
37
|
+
})
|
|
29
38
|
const { Factory } = await import(pathToFileURL(path).href)
|
|
30
39
|
|
|
31
40
|
return Factory
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { Locator } from '@toa.io/core'
|
|
5
|
+
|
|
6
|
+
import { storage } from './storage.js'
|
|
7
|
+
|
|
8
|
+
const manifest = (entity) => ({
|
|
9
|
+
locator: new Locator('one', 'test'),
|
|
10
|
+
path: import.meta.dirname,
|
|
11
|
+
entity
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const schema = { properties: {} }
|
|
15
|
+
|
|
16
|
+
it('should not create a storage for a component that stores nothing', async () => {
|
|
17
|
+
assert.equal(await storage(manifest(undefined), false), undefined)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should refuse migrations a storage does not apply', async () => {
|
|
21
|
+
const entity = {
|
|
22
|
+
schema,
|
|
23
|
+
storage: '@toa.io/storages.null',
|
|
24
|
+
migrations: [{ id: '0001', steps: [] }]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await assert.rejects(
|
|
28
|
+
storage(manifest(entity), false),
|
|
29
|
+
/declares migrations, which storage '@toa.io\/storages.null' does not apply/
|
|
30
|
+
)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should accept a storage with no migrations', async () => {
|
|
34
|
+
const entity = { schema, storage: '@toa.io/storages.null' }
|
|
35
|
+
|
|
36
|
+
assert.notEqual(await storage(manifest(entity), false), undefined)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should accept migrations a storage applies', async () => {
|
|
40
|
+
const entity = {
|
|
41
|
+
schema,
|
|
42
|
+
storage: '@toa.io/storages.mongodb',
|
|
43
|
+
migrations: [{ id: '0001', steps: [] }]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
assert.notEqual(await storage(manifest(entity), false), undefined)
|
|
47
|
+
})
|
package/types/bindings.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
import type { bindings } from '@toa.io/core'
|
|
1
|
+
import type { bindings } from '@toa.io/core/types'
|
|
2
2
|
|
|
3
|
-
export function broadcast<T>
|
|
3
|
+
export function broadcast<T>(
|
|
4
|
+
name: string,
|
|
5
|
+
group?: string,
|
|
6
|
+
binding?: string
|
|
7
|
+
): bindings.Broadcast<T>
|
package/types/index.d.ts
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
|
-
import * as core from '@toa.io/core'
|
|
1
|
+
import type * as core from '@toa.io/core'
|
|
2
|
+
import type * as types from '@toa.io/core/types'
|
|
2
3
|
import * as composition from './composition.js'
|
|
3
4
|
|
|
4
5
|
export * as bindings from './bindings.js'
|
|
5
6
|
|
|
6
|
-
export async function composition
|
|
7
|
+
export async function composition(
|
|
8
|
+
paths: string[],
|
|
9
|
+
options?: composition.Options
|
|
10
|
+
): Promise<core.Connector>
|
|
7
11
|
|
|
8
|
-
export async function remote
|
|
12
|
+
export async function remote(
|
|
13
|
+
locator: core.Locator,
|
|
14
|
+
source?: types.Source
|
|
15
|
+
): Promise<core.Remote>
|
|
9
16
|
|
|
10
|
-
export async function receive<T = any>
|
|
17
|
+
export async function receive<T = any>(
|
|
11
18
|
label: string,
|
|
12
19
|
receiver: Receiver
|
|
13
20
|
): Promise<core.Connector>
|
|
14
21
|
|
|
15
|
-
export async function receive<T = any>
|
|
22
|
+
export async function receive<T = any>(
|
|
16
23
|
label: string,
|
|
17
24
|
group: string | undefined,
|
|
18
25
|
receiver: Receiver
|
|
19
26
|
): Promise<core.Connector>
|
|
20
27
|
|
|
21
|
-
type Receiver = { receive: (message:
|
|
28
|
+
type Receiver = { receive: (message: types.Message<T>) => void | Promise<void> }
|
|
22
29
|
|
|
23
|
-
export function host
|
|
30
|
+
export function host(): types.extensions.Host
|
package/types/manifest.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import * as _norm from '@toa.io/norm/types'
|
|
|
2
2
|
import * as _composition from './composition.js'
|
|
3
3
|
|
|
4
4
|
declare namespace toa.boot {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
type Manifest = (
|
|
6
|
+
path: string,
|
|
7
|
+
options?: _composition.Options
|
|
8
|
+
) => Promise<_norm.Component>
|
|
8
9
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
# Change Log
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
-
|
|
6
|
-
# [1.0.0-alpha.283](https://github.com/toa-io/toa/compare/v1.0.0-alpha.282...v1.0.0-alpha.283) (2026-09-04)
|
|
7
|
-
|
|
8
|
-
### Bug Fixes
|
|
9
|
-
|
|
10
|
-
* **core:** an operation returns the errors it declares, and nothing else ([1c2b819](https://github.com/toa-io/toa/commit/1c2b8193ae7fbe86bc1a25053a6044ee4dd16a4a))
|
|
11
|
-
* **exposition:** declare the errors the built-in components return ([27200c3](https://github.com/toa-io/toa/commit/27200c348ab09068d58c52c3e83208cb29cd7290))
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# [1.0.0-alpha.282](https://github.com/toa-io/toa/compare/v1.0.0-alpha.281...v1.0.0-alpha.282) (2026-09-03)
|
|
15
|
-
|
|
16
|
-
### Bug Fixes
|
|
17
|
-
|
|
18
|
-
* **boot:** declare host, and build extensions with it in the step definitions ([915d957](https://github.com/toa-io/toa/commit/915d9571823358f8fca6fe19ef3aabd1b62e5605))
|
|
19
|
-
|
|
20
|
-
### Features
|
|
21
|
-
|
|
22
|
-
* **core:** a Host for extensions ([6315eac](https://github.com/toa-io/toa/commit/6315eac1e9fb05be472272de06385c54ad5934ce))
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# [1.0.0-alpha.278](https://github.com/toa-io/toa/compare/v1.0.0-alpha.277...v1.0.0-alpha.278) (2026-09-03)
|
|
26
|
-
|
|
27
|
-
**Note:** Version bump only for package @toa.io/boot
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# [1.0.0-alpha.277](https://github.com/toa-io/toa/compare/v1.0.0-alpha.276...v1.0.0-alpha.277) (2026-09-03)
|
|
34
|
-
|
|
35
|
-
### Bug Fixes
|
|
36
|
-
|
|
37
|
-
* read a manifest the way js-yaml read one before ([fcea1b2](https://github.com/toa-io/toa/commit/fcea1b21996d6efa6c3198acbfc9f70f1ce0c35c))
|
|
38
|
-
* settle what the module loader made asynchronous ([d4ab9df](https://github.com/toa-io/toa/commit/d4ab9dfb09e382da311dba08f52214e7645f4a6c))
|
|
39
|
-
* what the loader resolves, and what it will not ([1b8a02f](https://github.com/toa-io/toa/commit/1b8a02ff0a5e0f18ee62e3f71bc81116f86f9af1))
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# [1.0.0-alpha.274](https://github.com/toa-io/toa/compare/v1.0.0-alpha.273...v1.0.0-alpha.274) (2026-09-02)
|
|
43
|
-
|
|
44
|
-
**Note:** Version bump only for package @toa.io/boot
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# [1.0.0-alpha.273](https://github.com/toa-io/toa/compare/v1.0.0-alpha.272...v1.0.0-alpha.273) (2026-09-02)
|
|
51
|
-
|
|
52
|
-
**Note:** Version bump only for package @toa.io/boot
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# [1.0.0-alpha.272](https://github.com/toa-io/toa/compare/v1.0.0-alpha.271...v1.0.0-alpha.272) (2026-09-01)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
### Bug Fixes
|
|
62
|
-
|
|
63
|
-
* **amqp:** keep a receiver apart from the producers it delivers into ([4c2f43e](https://github.com/toa-io/toa/commit/4c2f43ebde68ba76a1fd277fe2a3305be1b622ef))
|
|
64
|
-
* **amqp:** tear a receiver down before its producers, and drop the split ([e6c1154](https://github.com/toa-io/toa/commit/e6c1154024df22f7a470c47c1a79bd170fe7e3b2))
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
* refactor(atomicity)!: take the lock manager from the stash ([4fd91eb](https://github.com/toa-io/toa/commit/4fd91eb2fdb68f7b87aaf16d182794bbc567ba57))
|
|
68
|
-
* refactor(atomicity)!: make the connector a family, not a partitioner ([06fd63a](https://github.com/toa-io/toa/commit/06fd63ad6296b724ef83afccf4e4e25d0bcaf080))
|
|
69
|
-
* refactor(atomicity)!: rename the connector and free it of the outbox ([21f1a41](https://github.com/toa-io/toa/commit/21f1a41aceafcfd35c7620014062fe46b54afa95))
|
|
70
|
-
* feat(core)!: commit events with the state that produced them ([1eb68cc](https://github.com/toa-io/toa/commit/1eb68cc435dbfa03faa16009fceb866693d22e1a)), closes [#20](https://github.com/toa-io/toa/issues/20)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
### Features
|
|
74
|
-
|
|
75
|
-
* **core:** give every component an atom aspect ([b501b9c](https://github.com/toa-io/toa/commit/b501b9cd3d5f5408d9faa71213e953fe2792cf9f))
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
### BREAKING CHANGES
|
|
79
|
-
|
|
80
|
-
* `context.stash.lock` is gone; lock through `context.atom` instead.
|
|
81
|
-
A stash pointer resolving to several addresses now uses the first.
|
|
82
|
-
|
|
83
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
84
|
-
* `Factory.partition(group)` is `Factory.atom(group)`, and the
|
|
85
|
-
`Partition` it returned is an `Atom`. `slots(total)` is unchanged.
|
|
86
|
-
|
|
87
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
88
|
-
* `@toa.io/partitions.redis` is now `@toa.io/atomicity`, and its
|
|
89
|
-
`lanes(total)` is `slots(total)`. Redis is declared as `atomicity` in the context
|
|
90
|
-
rather than `outbox.redis`, and read from `TOA_ATOMICITY_REDIS`.
|
|
91
|
-
`TOA_OUTBOX_PARTITION_INTERVAL` is `TOA_ATOMICITY_INTERVAL`.
|
|
92
|
-
|
|
93
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
94
|
-
* `event.changeset` is removed; use `origin` and `state`. `State`
|
|
95
|
-
takes an `Outbox` in place of an `Emission`. `difference` is dropped from
|
|
96
|
-
`@toa.io/generic` along with its last caller.
|
|
97
|
-
|
|
98
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
# [1.0.0-alpha.270](https://github.com/toa-io/toa/compare/v1.0.0-alpha.269...v1.0.0-alpha.270) (2026-08-31)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
### Performance Improvements
|
|
108
|
-
|
|
109
|
-
* **boot:** stop paying for what a composition does not need to start ([1619c27](https://github.com/toa-io/toa/commit/1619c2743072f4706939ce72f3074e615d26a91e))
|
|
110
|
-
* **openspan:** create a span only when something records it ([a6efb0e](https://github.com/toa-io/toa/commit/a6efb0eed38393f4c169b067f23d7447f0169bca))
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
# [1.0.0-alpha.266](https://github.com/toa-io/toa/compare/v1.0.0-alpha.265...v1.0.0-alpha.266) (2026-08-29)
|
|
117
|
-
|
|
118
|
-
**Note:** Version bump only for package @toa.io/boot
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# [1.0.0-alpha.265](https://github.com/toa-io/toa/compare/v1.0.0-alpha.264...v1.0.0-alpha.265) (2026-08-29)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
### Features
|
|
128
|
-
|
|
129
|
-
* **bridges.node:** add the dispose run command phase ([00df715](https://github.com/toa-io/toa/commit/00df7158f638b976ab09d4007102a4ead3c696c3))
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
# [1.0.0-alpha.264](https://github.com/toa-io/toa/compare/v1.0.0-alpha.263...v1.0.0-alpha.264) (2026-08-29)
|
|
136
|
-
|
|
137
|
-
**Note:** Version bump only for package @toa.io/boot
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
# [1.0.0-alpha.263](https://github.com/toa-io/toa/compare/v1.0.0-alpha.262...v1.0.0-alpha.263) (2026-08-29)
|
|
144
|
-
|
|
145
|
-
**Note:** Version bump only for package @toa.io/boot
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
# [1.0.0-alpha.262](https://github.com/toa-io/toa/compare/v1.0.0-alpha.261...v1.0.0-alpha.262) (2026-08-28)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
### Bug Fixes
|
|
155
|
-
|
|
156
|
-
* **boot:** let the composition own the process discovery ([2201e88](https://github.com/toa-io/toa/commit/2201e8852517fd1d2822c775c9b126eb60399580))
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
### Features
|
|
160
|
-
|
|
161
|
-
* **core:** identify the origin of a call with `request.source` ([9da6b66](https://github.com/toa-io/toa/commit/9da6b66df852690351a1e82c7b8cc176fd89774a))
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
# [1.0.0-alpha.259](https://github.com/toa-io/toa/compare/v1.0.0-alpha.258...v1.0.0-alpha.259) (2026-08-24)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
### Features
|
|
171
|
-
|
|
172
|
-
* **runtime:** split component RC into preflight and settle ([0d648b3](https://github.com/toa-io/toa/commit/0d648b314e4aac226c254bf01a86affa04b59b34))
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
# [1.0.0-alpha.257](https://github.com/toa-io/toa/compare/v1.0.0-alpha.256...v1.0.0-alpha.257) (2026-08-24)
|
|
179
|
-
|
|
180
|
-
**Note:** Version bump only for package @toa.io/boot
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
# [1.0.0-alpha.256](https://github.com/toa-io/toa/compare/v1.0.0-alpha.255...v1.0.0-alpha.256) (2026-08-23)
|
|
187
|
-
|
|
188
|
-
**Note:** Version bump only for package @toa.io/boot
|
package/src/exposition.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Exposition, Locator } from '@toa.io/core'
|
|
2
|
-
|
|
3
|
-
import * as boot from './index.js'
|
|
4
|
-
|
|
5
|
-
export const exposition = async (manifest) => {
|
|
6
|
-
const locator = new Locator('', '')
|
|
7
|
-
const exposition = new Exposition(locator, manifest)
|
|
8
|
-
const producers = boot.bindings.expose(exposition)
|
|
9
|
-
|
|
10
|
-
await Promise.all(producers.map((producer) => producer.connect()))
|
|
11
|
-
|
|
12
|
-
return producers
|
|
13
|
-
}
|