@toa.io/boot 1.0.0-alpha.28 → 1.0.0-alpha.283
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 +188 -0
- package/package.json +10 -8
- package/src/atom.js +37 -0
- package/src/atomicity.js +11 -0
- package/src/bindings/broadcast.js +3 -6
- package/src/bindings/constants.js +1 -3
- package/src/bindings/consume.js +5 -8
- package/src/bindings/emit.js +3 -6
- package/src/bindings/factory.js +5 -9
- package/src/bindings/index.js +5 -13
- package/src/bindings/produce.js +32 -19
- package/src/bindings/receive.js +3 -6
- package/src/bridge.js +23 -16
- package/src/call.js +6 -9
- package/src/cascade.js +6 -10
- package/src/component.js +56 -32
- package/src/composition.js +81 -16
- package/src/context.js +7 -8
- package/src/contract.js +12 -12
- package/src/discovery.js +7 -11
- package/src/emission.js +10 -14
- package/src/env.js +15 -15
- package/src/events.js +25 -0
- package/src/exposition.js +3 -7
- package/src/extensions/aspects.js +3 -7
- package/src/extensions/component.js +2 -6
- package/src/extensions/context.js +2 -6
- package/src/extensions/emitter.js +4 -7
- package/src/extensions/index.js +9 -19
- package/src/extensions/instances.js +1 -3
- package/src/extensions/load.js +6 -8
- package/src/extensions/manage.js +16 -0
- package/src/extensions/receiver.js +4 -7
- package/src/extensions/resolve.js +36 -11
- package/src/extensions/storage.js +2 -6
- package/src/extensions/tenants.js +4 -8
- package/src/guards.js +19 -0
- package/src/host.js +15 -0
- package/src/index.js +28 -32
- package/src/manifest.js +19 -14
- package/src/manifest.test.js +7 -7
- package/src/operation.js +8 -10
- package/src/outbox.js +17 -0
- package/src/rc.js +5 -0
- package/src/receivers.js +21 -18
- package/src/remote.js +28 -10
- package/src/span.js +17 -0
- package/src/storage.js +19 -13
- package/types/index.d.ts +7 -5
- package/types/manifest.d.ts +1 -1
package/src/composition.js
CHANGED
|
@@ -1,24 +1,89 @@
|
|
|
1
|
-
|
|
1
|
+
import { console } from 'openspan'
|
|
2
|
+
import { Composition } from '@toa.io/core'
|
|
3
|
+
import { version } from '@toa.io/runtime'
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
import * as boot from './index.js'
|
|
6
|
+
import { span } from './span.js'
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
async function composition (paths, options) {
|
|
8
|
+
export async function composition (paths, options) {
|
|
8
9
|
options = Object.assign({}, options)
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const components = await Promise.all(manifests.map(boot.component))
|
|
11
|
+
return span('boot composition', async () => {
|
|
12
|
+
const manifests = await span('load manifests',
|
|
13
|
+
async () => await Promise.all(paths.map((path) => boot.manifest(path, options))))
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
console.info('Starting composition', {
|
|
16
|
+
runtime: version,
|
|
17
|
+
components: manifests.map((manifest) => manifest.locator.id)
|
|
18
|
+
})
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
const tenants = await span('create tenants',
|
|
21
|
+
async () => await Promise.all(manifests.map(boot.extensions.tenants)))
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
const expositions = await span('expose discovery',
|
|
24
|
+
async () => await Promise.all(manifests.map(boot.discovery.expose)))
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const components = await span('create components',
|
|
28
|
+
async () => await Promise.all(manifests.map(boot.component)))
|
|
29
|
+
|
|
30
|
+
const groups = await Promise.all(components.map(async (component, index) =>
|
|
31
|
+
await boot.bindings.produce(component, manifests[index].operations)))
|
|
32
|
+
|
|
33
|
+
const receivers = await span('create receivers',
|
|
34
|
+
async () => await Promise.all(components.map((component, index) =>
|
|
35
|
+
boot.receivers(manifests[index], component))))
|
|
36
|
+
|
|
37
|
+
const producers = []
|
|
38
|
+
const settles = []
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < components.length; i++) {
|
|
41
|
+
const { local, other } = groups[i]
|
|
42
|
+
const settle = components[i].settle
|
|
43
|
+
const serving = [...local, ...other]
|
|
44
|
+
|
|
45
|
+
producers.push(...serving)
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
* A receiver turns an event into a call to an operation of its own component, and
|
|
49
|
+
* that call is served by these — the loop producer first, the broker's if it has
|
|
50
|
+
* already gone. They are siblings under the composition otherwise, so a receiver
|
|
51
|
+
* still draining a delivery would be left calling something already torn down.
|
|
52
|
+
*/
|
|
53
|
+
// one at a time: `depends` given an array links the group it makes rather than its
|
|
54
|
+
// members, and it is the members whose teardown has to wait for this one
|
|
55
|
+
for (const receiver of receivers[i])
|
|
56
|
+
for (const producer of serving) receiver.depends(producer)
|
|
23
57
|
|
|
24
|
-
|
|
58
|
+
if (settle === undefined)
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
if (local.length > 0)
|
|
62
|
+
settle.depends(local)
|
|
63
|
+
|
|
64
|
+
for (const producer of other)
|
|
65
|
+
producer.depends(settle)
|
|
66
|
+
|
|
67
|
+
for (const receiver of receivers[i])
|
|
68
|
+
receiver.depends(settle)
|
|
69
|
+
|
|
70
|
+
settles.push(settle)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const composition = new Composition(expositions.flat(), producers.concat(settles), receivers.flat(), tenants.flat())
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* A lookup that is never answered holds a connection open, and whoever
|
|
77
|
+
* awaits it has nothing to disconnect — the remote it would belong to does
|
|
78
|
+
* not exist yet. The composition owns the process's discovery instead, so
|
|
79
|
+
* an unanswered lookup cannot outlive it.
|
|
80
|
+
*/
|
|
81
|
+
composition.depends(await boot.discovery.discovery())
|
|
82
|
+
|
|
83
|
+
return boot.extensions.manage(composition)
|
|
84
|
+
} catch (exception) {
|
|
85
|
+
await Promise.all(expositions.flat().map((connector) => connector.disconnect(true)))
|
|
86
|
+
throw exception
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
package/src/context.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import { Context, Locator } from '@toa.io/core'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import * as boot from './index.js'
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const context = async (manifest) => {
|
|
8
|
-
const local = await boot.remote(manifest.locator, manifest)
|
|
5
|
+
export const context = async (manifest) => {
|
|
6
|
+
const local = await boot.remote(manifest.locator, undefined, manifest)
|
|
9
7
|
const aspects = boot.extensions.aspects(manifest)
|
|
10
8
|
|
|
9
|
+
// a system aspect: it is there in every component, declared by none of them
|
|
10
|
+
aspects.push(boot.atom(manifest.locator.id))
|
|
11
|
+
|
|
11
12
|
const lookup = async (namespace, name) => {
|
|
12
13
|
const locator = new Locator(name, namespace)
|
|
13
14
|
const remote = await boot.remote(locator)
|
|
@@ -21,5 +22,3 @@ const context = async (manifest) => {
|
|
|
21
22
|
|
|
22
23
|
return boot.extensions.context(context)
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
exports.context = context
|
package/src/contract.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import { contract } from '@toa.io/core'
|
|
2
|
+
import * as schemas from '@toa.io/schemas'
|
|
2
3
|
|
|
3
|
-
const {
|
|
4
|
-
const { Schema } = require('@toa.io/schema')
|
|
4
|
+
const { Request, Reply } = contract
|
|
5
5
|
|
|
6
|
-
const request = (definition, entity) => {
|
|
6
|
+
export const request = (definition, entity) => {
|
|
7
7
|
const request = Request.schema(definition, entity)
|
|
8
|
-
const schema =
|
|
8
|
+
const schema = schemas.schema(request, { removeAdditional: true })
|
|
9
9
|
|
|
10
|
-
return new Request(schema)
|
|
10
|
+
return new Request(schema, definition)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const reply = (output,
|
|
14
|
-
const reply = Reply.schema(output,
|
|
15
|
-
|
|
13
|
+
export const reply = (output, errors) => {
|
|
14
|
+
const reply = Reply.schema(output, errors)
|
|
15
|
+
|
|
16
|
+
// a reply is read, not shaped: what the operation returned is what the caller gets,
|
|
17
|
+
// and a value that only fits once coerced does not fit
|
|
18
|
+
const schema = schemas.schema(reply, { coerceTypes: false, useDefaults: false })
|
|
16
19
|
|
|
17
20
|
return new Reply(schema)
|
|
18
21
|
}
|
|
19
|
-
|
|
20
|
-
exports.request = request
|
|
21
|
-
exports.reply = reply
|
package/src/discovery.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { Discovery, Exposition } from '@toa.io/core'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const boot = require('./index')
|
|
3
|
+
import * as boot from './index.js'
|
|
6
4
|
|
|
7
5
|
let promise
|
|
8
6
|
let instance = null
|
|
9
7
|
|
|
10
|
-
const discovery = async () => {
|
|
8
|
+
export const discovery = async () => {
|
|
11
9
|
if (instance === null) {
|
|
12
10
|
instance = new Discovery(lookup)
|
|
13
11
|
promise = instance.connect()
|
|
@@ -19,17 +17,18 @@ const discovery = async () => {
|
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
const lookup = async (locator) => {
|
|
22
|
-
const call = boot.call(locator, ENDPOINT, { bindings: BINDINGS })
|
|
20
|
+
const call = await boot.call(locator, ENDPOINT, { bindings: BINDINGS })
|
|
23
21
|
|
|
24
22
|
await call.connect()
|
|
25
23
|
|
|
26
24
|
return call
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
const expose = async (manifest) => {
|
|
27
|
+
export const expose = async (manifest) => {
|
|
30
28
|
const exposition = new Exposition(manifest.locator, manifest)
|
|
31
29
|
const operations = { [ENDPOINT]: { bindings: BINDINGS } }
|
|
32
|
-
const
|
|
30
|
+
const { local, other } = await boot.bindings.produce(exposition, operations)
|
|
31
|
+
const producers = local.concat(other)
|
|
33
32
|
|
|
34
33
|
await Promise.all(producers.map((producer) => producer.connect()))
|
|
35
34
|
|
|
@@ -38,6 +37,3 @@ const expose = async (manifest) => {
|
|
|
38
37
|
|
|
39
38
|
const BINDINGS = ['@toa.io/bindings.amqp']
|
|
40
39
|
const ENDPOINT = '.lookup'
|
|
41
|
-
|
|
42
|
-
exports.discovery = discovery
|
|
43
|
-
exports.expose = expose
|
package/src/emission.js
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import { Emission, Event } from '@toa.io/core'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import * as boot from './index.js'
|
|
4
|
+
import * as extensions from './extensions/index.js'
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
const extensions = require('./extensions')
|
|
7
|
-
|
|
8
|
-
const emission = (definitions, locator, context) => {
|
|
6
|
+
export const emission = async (definitions, locator, context) => {
|
|
9
7
|
if (definitions === undefined) return
|
|
10
8
|
|
|
11
|
-
const events = Object.entries(definitions).map(([label, definition]) => {
|
|
12
|
-
const emitter = boot.bindings.emit(definition.binding, locator, label)
|
|
13
|
-
const decorator = extensions.emitter(emitter, label)
|
|
14
|
-
const bridge = boot.bridge.event(definition.bridge, definition.path, label, context)
|
|
9
|
+
const events = await Promise.all(Object.entries(definitions).map(async ([label, definition]) => {
|
|
10
|
+
const emitter = await boot.bindings.emit(definition.binding, locator, label)
|
|
11
|
+
const decorator = extensions.emitter(emitter, label, locator)
|
|
12
|
+
const bridge = await boot.bridge.event(definition.bridge, definition.path, label, context)
|
|
15
13
|
|
|
16
|
-
return new Event(definition, decorator, bridge)
|
|
17
|
-
})
|
|
14
|
+
return new Event({ ...definition, label: `${locator.id}.${label}` }, decorator, bridge)
|
|
15
|
+
}))
|
|
18
16
|
|
|
19
17
|
return new Emission(events)
|
|
20
18
|
}
|
|
21
|
-
|
|
22
|
-
exports.emission = emission
|
package/src/env.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
1
|
+
import dotenv from 'dotenv'
|
|
2
|
+
import { findUp } from '@toa.io/generic'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* A local run reads what `toa env` wrote. `dotenv` leaves a variable that is already set
|
|
6
|
+
* alone, so what the environment says still wins and reading the file costs nothing where
|
|
7
|
+
* there is none — a deployment has no `.env` to find.
|
|
8
|
+
*
|
|
9
|
+
* It used to be read only where `TOA_ENV` was unset, taking that one variable as proof the
|
|
10
|
+
* environment had been injected. Setting it for any other reason then dropped the file
|
|
11
|
+
* whole, and every variable in it with it.
|
|
12
|
+
*/
|
|
13
|
+
const path = findUp('.env')
|
|
14
|
+
|
|
15
|
+
if (path !== undefined) dotenv.config({ path })
|
package/src/events.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The events of this component that something consumes. An event nobody consumes is not
|
|
3
|
+
* published, so it gets no emitter and no exchange, and a component none of whose events are
|
|
4
|
+
* consumed gets no emission — and therefore no outbox and no transaction.
|
|
5
|
+
*
|
|
6
|
+
* The deployment computes this from the whole context. Without one — a local run, `toa mono`,
|
|
7
|
+
* a composition booted in a test — the variable is absent and every event is published.
|
|
8
|
+
*
|
|
9
|
+
* @param {toa.norm.Component} manifest
|
|
10
|
+
* @returns {toa.norm.Events | undefined}
|
|
11
|
+
*/
|
|
12
|
+
export const events = (manifest) => {
|
|
13
|
+
if (manifest.events === undefined) return
|
|
14
|
+
|
|
15
|
+
const value = process.env[VARIABLE + manifest.locator.uppercase]
|
|
16
|
+
|
|
17
|
+
if (value === undefined) return manifest.events
|
|
18
|
+
|
|
19
|
+
const consumed = new Set(value.split(' ').filter(Boolean))
|
|
20
|
+
const entries = Object.entries(manifest.events).filter(([label]) => consumed.has(label))
|
|
21
|
+
|
|
22
|
+
return entries.length === 0 ? undefined : Object.fromEntries(entries)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const VARIABLE = 'TOA_EVENTS_'
|
package/src/exposition.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { Exposition, Locator } from '@toa.io/core'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import * as boot from './index.js'
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const exposition = async (manifest) => {
|
|
5
|
+
export const exposition = async (manifest) => {
|
|
8
6
|
const locator = new Locator('', '')
|
|
9
7
|
const exposition = new Exposition(locator, manifest)
|
|
10
8
|
const producers = boot.bindings.expose(exposition)
|
|
@@ -13,5 +11,3 @@ const exposition = async (manifest) => {
|
|
|
13
11
|
|
|
14
12
|
return producers
|
|
15
13
|
}
|
|
16
|
-
|
|
17
|
-
exports.exposition = exposition
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { resolve } = require('./resolve')
|
|
1
|
+
import { instance } from './resolve.js'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param {toa.norm.Component} manifest
|
|
7
5
|
* @returns {toa.core.extensions.Aspect[]}
|
|
8
6
|
*/
|
|
9
|
-
const aspects = (manifest) => {
|
|
7
|
+
export const aspects = (manifest) => {
|
|
10
8
|
const aspects = []
|
|
11
9
|
|
|
12
10
|
if (manifest.extensions === undefined) return aspects
|
|
13
11
|
|
|
14
12
|
for (const [name, declaration] of Object.entries(manifest.extensions)) {
|
|
15
|
-
const factory =
|
|
13
|
+
const factory = instance(name, manifest.path)
|
|
16
14
|
|
|
17
15
|
if (factory.aspect === undefined) continue
|
|
18
16
|
|
|
@@ -24,5 +22,3 @@ const aspects = (manifest) => {
|
|
|
24
22
|
|
|
25
23
|
return aspects
|
|
26
24
|
}
|
|
27
|
-
|
|
28
|
-
exports.aspects = aspects
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { instances } = require('./instances')
|
|
1
|
+
import { instances } from './instances.js'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param {toa.core.Component} component
|
|
7
5
|
* @returns {toa.core.Component}
|
|
8
6
|
*/
|
|
9
|
-
const component = (component) => {
|
|
7
|
+
export const component = (component) => {
|
|
10
8
|
let decorated = component
|
|
11
9
|
|
|
12
10
|
for (const factory of Object.values(instances)) {
|
|
@@ -15,5 +13,3 @@ const component = (component) => {
|
|
|
15
13
|
|
|
16
14
|
return decorated
|
|
17
15
|
}
|
|
18
|
-
|
|
19
|
-
exports.component = component
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { instances } = require('./instances')
|
|
1
|
+
import { instances } from './instances.js'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param {toa.core.Context} component
|
|
7
5
|
* @returns {toa.core.Context}
|
|
8
6
|
*/
|
|
9
|
-
const context = (component) => {
|
|
7
|
+
export const context = (component) => {
|
|
10
8
|
let decorated = component
|
|
11
9
|
|
|
12
10
|
for (const factory of Object.values(instances)) {
|
|
@@ -15,5 +13,3 @@ const context = (component) => {
|
|
|
15
13
|
|
|
16
14
|
return decorated
|
|
17
15
|
}
|
|
18
|
-
|
|
19
|
-
exports.context = context
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { instances } = require('./instances')
|
|
1
|
+
import { instances } from './instances.js'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param {toa.core.bindings.Emitter} emitter
|
|
7
5
|
* @param {string} label
|
|
6
|
+
* @param {toa.core.Locator} locator
|
|
8
7
|
* @returns {toa.core.bindings.Emitter}
|
|
9
8
|
*/
|
|
10
|
-
const emitter = (emitter, label) => {
|
|
9
|
+
export const emitter = (emitter, label, locator) => {
|
|
11
10
|
let decorated = emitter
|
|
12
11
|
|
|
13
12
|
for (const factory of Object.values(instances)) {
|
|
14
|
-
if (factory.emitter !== undefined) decorated = factory.emitter(decorated, label)
|
|
13
|
+
if (factory.emitter !== undefined) decorated = factory.emitter(decorated, label, locator)
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
return decorated
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
exports.emitter = emitter
|
package/src/extensions/index.js
CHANGED
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const { tenants } = require('./tenants')
|
|
11
|
-
|
|
12
|
-
exports.aspects = aspects
|
|
13
|
-
exports.component = component
|
|
14
|
-
exports.context = context
|
|
15
|
-
exports.emitter = emitter
|
|
16
|
-
exports.load = load
|
|
17
|
-
exports.receiver = receiver
|
|
18
|
-
exports.storage = storage
|
|
19
|
-
exports.tenants = tenants
|
|
1
|
+
export { aspects } from './aspects.js'
|
|
2
|
+
export { component } from './component.js'
|
|
3
|
+
export { context } from './context.js'
|
|
4
|
+
export { emitter } from './emitter.js'
|
|
5
|
+
export { load } from './load.js'
|
|
6
|
+
export { manage } from './manage.js'
|
|
7
|
+
export { receiver } from './receiver.js'
|
|
8
|
+
export { storage } from './storage.js'
|
|
9
|
+
export { tenants } from './tenants.js'
|
package/src/extensions/load.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { resolve } = require('./resolve')
|
|
1
|
+
import { resolve } from './resolve.js'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param {toa.norm.Component} manifest
|
|
7
5
|
*/
|
|
8
|
-
const load = (manifest) => {
|
|
9
|
-
if (manifest.extensions === undefined)
|
|
6
|
+
export const load = async (manifest) => {
|
|
7
|
+
if (manifest.extensions === undefined)
|
|
8
|
+
return
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
await Promise.all(Object.keys(manifest.extensions)
|
|
11
|
+
.map(async (name) => await resolve(name, manifest.path)))
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
exports.load = load
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { instances } from './instances.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {toa.core.Connector} composition
|
|
5
|
+
* @returns {toa.core.Connector}
|
|
6
|
+
*/
|
|
7
|
+
export const manage = (composition) => {
|
|
8
|
+
let managed = composition
|
|
9
|
+
|
|
10
|
+
for (const factory of Object.values(instances)) {
|
|
11
|
+
if (factory.manage !== undefined)
|
|
12
|
+
managed = factory.manage(managed)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return managed
|
|
16
|
+
}
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { instances } = require('./instances')
|
|
1
|
+
import { instances } from './instances.js'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param {toa.core.Receiver} receiver
|
|
7
5
|
* @param {toa.core.Locator} locator
|
|
8
6
|
* @returns {toa.core.Receiver}
|
|
9
7
|
*/
|
|
10
|
-
const receiver = (receiver, locator) => {
|
|
8
|
+
export const receiver = (receiver, locator) => {
|
|
11
9
|
let decorated = receiver
|
|
12
10
|
|
|
13
11
|
for (const factory of Object.values(instances)) {
|
|
14
|
-
if (factory.receiver !== undefined)
|
|
12
|
+
if (factory.receiver !== undefined)
|
|
13
|
+
decorated = factory.receiver(decorated, locator)
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
return decorated
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
exports.receiver = receiver
|
|
@@ -1,27 +1,52 @@
|
|
|
1
|
-
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import { pathToFileURL } from 'node:url'
|
|
3
|
+
import { find } from '@toa.io/generic'
|
|
4
|
+
import * as boot from '../../src/index.js'
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
const boot = require('../..')
|
|
6
|
+
import { instances } from './instances.js'
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
// what `find` returns is a package directory, and a module is loaded by file
|
|
9
|
+
const require = createRequire(import.meta.url)
|
|
10
|
+
|
|
11
|
+
// an extension is loaded once, and what is remembered while it loads is the
|
|
12
|
+
// promise, so two components cannot each make a factory
|
|
13
|
+
const loading = {}
|
|
7
14
|
|
|
8
15
|
/**
|
|
16
|
+
* Loads an extension and remembers its factory.
|
|
17
|
+
*
|
|
9
18
|
* @param {string} reference
|
|
10
19
|
* @param {string} base
|
|
11
|
-
* @returns {toa.core.extensions.Factory}
|
|
20
|
+
* @returns {Promise<toa.core.extensions.Factory>}
|
|
12
21
|
*/
|
|
13
|
-
const resolve = (reference, base = process.cwd()) => {
|
|
22
|
+
export const resolve = async (reference, base = process.cwd()) => {
|
|
14
23
|
const path = find(reference, base)
|
|
15
24
|
|
|
16
|
-
|
|
25
|
+
loading[path] ??= create(path)
|
|
26
|
+
instances[path] = await loading[path]
|
|
17
27
|
|
|
18
28
|
return instances[path]
|
|
19
29
|
}
|
|
20
30
|
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The factory of an extension already loaded. Everything that decorates what a
|
|
33
|
+
* component is made of runs after its extensions are loaded, and stays
|
|
34
|
+
* synchronous.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} reference
|
|
37
|
+
* @param {string} base
|
|
38
|
+
* @returns {toa.core.extensions.Factory}
|
|
39
|
+
*/
|
|
40
|
+
export const instance = (reference, base = process.cwd()) => {
|
|
41
|
+
const factory = instances[find(reference, base)]
|
|
42
|
+
|
|
43
|
+
if (factory === undefined) throw new Error(`Extension '${reference}' is not loaded`)
|
|
23
44
|
|
|
24
|
-
return
|
|
45
|
+
return factory
|
|
25
46
|
}
|
|
26
47
|
|
|
27
|
-
|
|
48
|
+
const create = async (path) => {
|
|
49
|
+
const { Factory } = await import(pathToFileURL(require.resolve(path)).href)
|
|
50
|
+
|
|
51
|
+
return new Factory(boot.host())
|
|
52
|
+
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { instances } = require('./instances')
|
|
1
|
+
import { instances } from './instances.js'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param {toa.core.Storage} storage
|
|
7
5
|
* @returns {toa.core.Storage}
|
|
8
6
|
*/
|
|
9
|
-
const storage = (storage) => {
|
|
7
|
+
export const storage = (storage) => {
|
|
10
8
|
let decorated = storage
|
|
11
9
|
|
|
12
10
|
for (const factory of Object.values(instances)) {
|
|
@@ -15,5 +13,3 @@ const storage = (storage) => {
|
|
|
15
13
|
|
|
16
14
|
return decorated
|
|
17
15
|
}
|
|
18
|
-
|
|
19
|
-
exports.storage = storage
|
|
@@ -1,27 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { resolve } = require('./resolve')
|
|
1
|
+
import { resolve } from './resolve.js'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param {toa.norm.Component} manifest
|
|
7
5
|
* @returns {toa.core.Connector[]}
|
|
8
6
|
*/
|
|
9
|
-
const tenants = (manifest) => {
|
|
7
|
+
export const tenants = async (manifest) => {
|
|
10
8
|
const tenants = []
|
|
11
9
|
|
|
12
10
|
if (manifest.extensions === undefined) return tenants
|
|
13
11
|
|
|
14
12
|
for (const [name, declaration] of Object.entries(manifest.extensions)) {
|
|
15
|
-
const factory = resolve(name, manifest.path)
|
|
13
|
+
const factory = await resolve(name, manifest.path)
|
|
16
14
|
|
|
17
15
|
if (factory.tenant === undefined) continue
|
|
18
16
|
|
|
19
|
-
const tenant = factory.tenant(manifest.locator, declaration)
|
|
17
|
+
const tenant = await factory.tenant(manifest.locator, declaration, manifest)
|
|
20
18
|
|
|
21
19
|
tenants.push(tenant)
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
return tenants
|
|
25
23
|
}
|
|
26
|
-
|
|
27
|
-
exports.tenants = tenants
|