@toa.io/boot 1.0.0-alpha.25 → 1.0.0-alpha.252

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/boot",
3
- "version": "1.0.0-alpha.25",
3
+ "version": "1.0.0-alpha.252",
4
4
  "description": "Toa Boot",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -20,13 +20,14 @@
20
20
  "test": "echo \"Error: run tests from root\" && exit 1"
21
21
  },
22
22
  "dependencies": {
23
- "@toa.io/core": "1.0.0-alpha.25",
24
- "@toa.io/filesystem": "1.0.0-alpha.25",
25
- "@toa.io/generic": "1.0.0-alpha.25",
26
- "@toa.io/norm": "1.0.0-alpha.25",
27
- "@toa.io/schema": "1.0.0-alpha.25",
23
+ "@toa.io/core": "1.0.0-alpha.252",
24
+ "@toa.io/filesystem": "1.0.0-alpha.225",
25
+ "@toa.io/generic": "1.0.0-alpha.225",
26
+ "@toa.io/norm": "1.0.0-alpha.252",
27
+ "@toa.io/schemas": "1.0.0-alpha.246",
28
28
  "clone-deep": "4.0.1",
29
- "dotenv": "16.1.1"
29
+ "dotenv": "16.1.1",
30
+ "openspan": "1.0.0-alpha.252"
30
31
  },
31
- "gitHead": "756ea259f35dda03d45047f8153d441191026877"
32
+ "gitHead": "753e94e44b499be02dee28fa1db2d45c053434cf"
32
33
  }
@@ -9,18 +9,19 @@ const produce = (component, operations) => group(operations, (factory, endpoints
9
9
  const group = (operations, callback) => {
10
10
  const map = {}
11
11
 
12
- for (const [endpoint, operation] of Object.entries(operations)) {
13
- // noinspection JSUnresolvedVariable
14
- const bindings = global.TOA_INTEGRATION_BINDINGS_LOOP_DISABLED
15
- ? operation.bindings
16
- : [LOOP].concat(operation.bindings)
17
-
18
- for (const binding of bindings) {
19
- if (!map[binding]) map[binding] = []
20
-
21
- map[binding].push(endpoint)
12
+ if (operations !== undefined)
13
+ for (const [endpoint, operation] of Object.entries(operations)) {
14
+ // noinspection JSUnresolvedVariable
15
+ const bindings = global.TOA_INTEGRATION_BINDINGS_LOOP_DISABLED
16
+ ? operation.bindings
17
+ : [LOOP].concat(operation.bindings)
18
+
19
+ for (const binding of bindings) {
20
+ if (!map[binding]) map[binding] = []
21
+
22
+ map[binding].push(endpoint)
23
+ }
22
24
  }
23
- }
24
25
 
25
26
  return Object.entries(map).map(([binding, endpoints]) => callback(factory(binding), endpoints))
26
27
  }
package/src/bridge.js CHANGED
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const algorithm = (bridge, path, endpoint, context) => {
4
- const algorithm = resolve(bridge).algorithm(path, endpoint, context)
3
+ async function algorithm (bridge, path, endpoint, context) {
4
+ const factory = resolve(bridge)
5
+ const algorithm = await factory.algorithm(path, endpoint, context)
5
6
 
6
7
  algorithm.depends(context)
7
8
 
@@ -10,6 +11,16 @@ const algorithm = (bridge, path, endpoint, context) => {
10
11
 
11
12
  const event = (bridge, path, label, context) => resolve(bridge).event(path, label, context)
12
13
  const receiver = (bridge, path, label) => resolve(bridge).receiver(path, label)
14
+ const guard = (bridge, path, label, context) => resolve(bridge).guard(path, label, context)
15
+
16
+ async function rc (bridge, path, context) {
17
+ const factory = resolve(bridge)
18
+
19
+ if (factory.rc === undefined)
20
+ return
21
+
22
+ return factory.rc(path, context)
23
+ }
13
24
 
14
25
  const factories = {}
15
26
 
@@ -26,3 +37,5 @@ const resolve = (bridge) => {
26
37
  exports.algorithm = algorithm
27
38
  exports.event = event
28
39
  exports.receiver = receiver
40
+ exports.guard = guard
41
+ exports.rc = rc
package/src/cascade.js CHANGED
@@ -4,13 +4,13 @@ const { Cascade } = require('@toa.io/core')
4
4
 
5
5
  const boot = require('./index')
6
6
 
7
- const cascade = (manifest, endpoint, definition, context) => {
7
+ async function cascade (manifest, endpoint, definition, context, rc) {
8
8
  const bridges = []
9
9
 
10
10
  if (definition.forward) endpoint = definition.forward
11
11
 
12
12
  if (definition.bridge) {
13
- const bridge = boot.bridge.algorithm(definition.bridge, manifest.path, endpoint, context)
13
+ const bridge = await boot.bridge.algorithm(definition.bridge, manifest.path, endpoint, context)
14
14
 
15
15
  bridges.unshift(bridge)
16
16
  }
@@ -22,12 +22,12 @@ const cascade = (manifest, endpoint, definition, context) => {
22
22
 
23
23
  if (operation === undefined) continue
24
24
 
25
- const bridge = boot.bridge.algorithm(operation.bridge, prototype.path, endpoint, context)
25
+ const bridge = await boot.bridge.algorithm(operation.bridge, prototype.path, endpoint, context)
26
26
 
27
27
  bridges.unshift(bridge)
28
28
  }
29
29
 
30
- return new Cascade(bridges)
30
+ return new Cascade(bridges, rc)
31
31
  }
32
32
 
33
33
  exports.cascade = cascade
package/src/component.js CHANGED
@@ -1,24 +1,19 @@
1
1
  'use strict'
2
2
 
3
- const { remap } = require('@toa.io/generic')
4
- const {
5
- Component,
6
- Locator,
7
- State,
8
- entities
9
- } = require('@toa.io/core')
10
- const { Schema } = require('@toa.io/schema')
3
+ const { Component, Locator, State, entities } = require('@toa.io/core')
4
+ const schemas = require('@toa.io/schemas')
11
5
 
12
6
  const boot = require('./index')
7
+ const { span } = require('./span')
13
8
 
14
- /**
15
- * @param {toa.norm.Component} manifest
16
- * @returns {Promise<toa.core.Component>}
17
- */
18
9
  const component = async (manifest) => {
19
- boot.extensions.load(manifest)
20
-
21
10
  const locator = new Locator(manifest.name, manifest.namespace)
11
+
12
+ return span(`component ${locator.id}`, () => create(manifest, locator))
13
+ }
14
+
15
+ const create = async (manifest, locator) => {
16
+ boot.extensions.load(manifest)
22
17
  const storage = boot.storage(manifest)
23
18
  const context = await boot.context(manifest)
24
19
  const emission = boot.emission(manifest.events, locator, context)
@@ -26,17 +21,15 @@ const component = async (manifest) => {
26
21
  let state
27
22
 
28
23
  if (manifest.entity !== undefined) {
29
- const schema = new Schema(manifest.entity.schema)
30
- const entity = new entities.Factory(schema)
24
+ const schema = schemas.schema(manifest.entity.schema)
25
+ const guards = await boot.guards(manifest, context)
26
+ const entity = new entities.Factory(schema, guards)
31
27
 
32
28
  state = new State(storage, entity, emission, manifest.entity.associated)
33
29
  }
34
30
 
35
- const operations = manifest.operations === undefined
36
- ? {}
37
- : remap(manifest.operations, (definition, endpoint) =>
38
- boot.operation(manifest, endpoint, definition, context, state))
39
-
31
+ const rc = await boot.rc(manifest, context)
32
+ const operations = await bootOperations(manifest, context, state, rc)
40
33
  const component = new Component(locator, operations)
41
34
 
42
35
  if (storage) component.depends(storage)
@@ -45,4 +38,16 @@ const component = async (manifest) => {
45
38
  return boot.extensions.component(component)
46
39
  }
47
40
 
41
+ async function bootOperations (manifest, context, state, rc) {
42
+ if (manifest.operations === undefined)
43
+ return {}
44
+
45
+ const operations = {}
46
+
47
+ for (const [endpoint, definition] of Object.entries(manifest.operations))
48
+ operations[endpoint] = await boot.operation(manifest, endpoint, definition, context, state, rc)
49
+
50
+ return operations
51
+ }
52
+
48
53
  exports.component = component
@@ -1,24 +1,44 @@
1
1
  'use strict'
2
2
 
3
+ const { console } = require('openspan')
3
4
  const { Composition } = require('@toa.io/core')
5
+ const { version } = require('@toa.io/runtime/package.json')
4
6
 
5
7
  const boot = require('./index')
8
+ const { span } = require('./span')
6
9
 
7
10
  async function composition (paths, options) {
8
11
  options = Object.assign({}, options)
9
12
 
10
- const manifests = await Promise.all(paths.map((path) => boot.manifest(path, options)))
11
- const tenants = await Promise.all(manifests.map(boot.extensions.tenants))
12
- const expositions = await Promise.all(manifests.map(boot.discovery.expose))
13
- const components = await Promise.all(manifests.map(boot.component))
13
+ return span('boot composition', async () => {
14
+ const manifests = await span('load manifests',
15
+ async () => await Promise.all(paths.map((path) => boot.manifest(path, options))))
14
16
 
15
- const producers = components.map((component, index) =>
16
- boot.bindings.produce(component, manifests[index].operations))
17
+ console.info('Starting composition', {
18
+ runtime: version,
19
+ components: manifests.map((manifest) => manifest.locator.id)
20
+ })
17
21
 
18
- const receivers = await Promise.all(components.map((component, index) =>
19
- boot.receivers(manifests[index], component)))
22
+ const tenants = await span('create tenants',
23
+ async () => await Promise.all(manifests.map(boot.extensions.tenants)))
20
24
 
21
- return new Composition(expositions.flat(), producers.flat(), receivers.flat(), tenants.flat())
25
+ const expositions = await span('expose discovery',
26
+ async () => await Promise.all(manifests.map(boot.discovery.expose)))
27
+
28
+ const components = await span('create components',
29
+ async () => await Promise.all(manifests.map(boot.component)))
30
+
31
+ const producers = components.map((component, index) =>
32
+ boot.bindings.produce(component, manifests[index].operations))
33
+
34
+ const receivers = await span('create receivers',
35
+ async () => await Promise.all(components.map((component, index) =>
36
+ boot.receivers(manifests[index], component))))
37
+
38
+ const composition = new Composition(expositions.flat(), producers.flat(), receivers.flat(), tenants.flat())
39
+
40
+ return boot.extensions.manage(composition)
41
+ })
22
42
  }
23
43
 
24
44
  exports.composition = composition
package/src/contract.js CHANGED
@@ -1,18 +1,18 @@
1
1
  'use strict'
2
2
 
3
3
  const { contract: { Request, Reply } } = require('@toa.io/core')
4
- const { Schema } = require('@toa.io/schema')
4
+ const schemas = require('@toa.io/schemas')
5
5
 
6
6
  const request = (definition, entity) => {
7
7
  const request = Request.schema(definition, entity)
8
- const schema = new Schema(request, { removeAdditional: true }) // soft inputs
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, error) => {
14
- const reply = Reply.schema(output, error)
15
- const schema = new Schema(reply) // outputs strict
13
+ const reply = (output, errors) => {
14
+ const reply = Reply.schema(output, errors)
15
+ const schema = schemas.schema(reply)
16
16
 
17
17
  return new Reply(schema)
18
18
  }
package/src/emission.js CHANGED
@@ -13,7 +13,7 @@ const emission = (definitions, locator, context) => {
13
13
  const decorator = extensions.emitter(emitter, label)
14
14
  const bridge = boot.bridge.event(definition.bridge, definition.path, label, context)
15
15
 
16
- return new Event(definition, decorator, bridge)
16
+ return new Event({ ...definition, label: `${locator.id}.${label}` }, decorator, bridge)
17
17
  })
18
18
 
19
19
  return new Emission(events)
@@ -5,6 +5,7 @@ const { component } = require('./component')
5
5
  const { context } = require('./context')
6
6
  const { emitter } = require('./emitter')
7
7
  const { load } = require('./load')
8
+ const { manage } = require('./manage')
8
9
  const { receiver } = require('./receiver')
9
10
  const { storage } = require('./storage')
10
11
  const { tenants } = require('./tenants')
@@ -14,6 +15,7 @@ exports.component = component
14
15
  exports.context = context
15
16
  exports.emitter = emitter
16
17
  exports.load = load
18
+ exports.manage = manage
17
19
  exports.receiver = receiver
18
20
  exports.storage = storage
19
21
  exports.tenants = tenants
@@ -6,9 +6,11 @@ const { resolve } = require('./resolve')
6
6
  * @param {toa.norm.Component} manifest
7
7
  */
8
8
  const load = (manifest) => {
9
- if (manifest.extensions === undefined) return
9
+ if (manifest.extensions === undefined)
10
+ return
10
11
 
11
- for (const name of Object.keys(manifest.extensions)) resolve(name, manifest.path)
12
+ for (const name of Object.keys(manifest.extensions))
13
+ resolve(name, manifest.path)
12
14
  }
13
15
 
14
16
  exports.load = load
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ const { instances } = require('./instances')
4
+
5
+ /**
6
+ * @param {toa.core.Connector} composition
7
+ * @returns {toa.core.Connector}
8
+ */
9
+ const manage = (composition) => {
10
+ let managed = composition
11
+
12
+ for (const factory of Object.values(instances)) {
13
+ if (factory.manage !== undefined)
14
+ managed = factory.manage(managed)
15
+ }
16
+
17
+ return managed
18
+ }
19
+
20
+ exports.manage = manage
@@ -11,7 +11,8 @@ const receiver = (receiver, locator) => {
11
11
  let decorated = receiver
12
12
 
13
13
  for (const factory of Object.values(instances)) {
14
- if (factory.receiver !== undefined) decorated = factory.receiver(decorated, locator)
14
+ if (factory.receiver !== undefined)
15
+ decorated = factory.receiver(decorated, locator)
15
16
  }
16
17
 
17
18
  return decorated
@@ -16,7 +16,7 @@ const tenants = (manifest) => {
16
16
 
17
17
  if (factory.tenant === undefined) continue
18
18
 
19
- const tenant = factory.tenant(manifest.locator, declaration)
19
+ const tenant = factory.tenant(manifest.locator, declaration, manifest)
20
20
 
21
21
  tenants.push(tenant)
22
22
  }
package/src/guards.js ADDED
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ const { Guard } = require('@toa.io/core')
4
+ const boot = require('./index')
5
+
6
+ async function guards(manifest, context) {
7
+ if (manifest.guards === undefined)
8
+ return
9
+
10
+ const entries = Object.entries(manifest.guards)
11
+
12
+ return await Promise.all(entries.map(async ([name, guard]) => {
13
+ const bridge = await load(guard, name, context)
14
+
15
+ return new Guard(name, bridge)
16
+ }))
17
+ }
18
+
19
+ async function load(guard, name, context) {
20
+ return await boot.bridge.guard(guard.bridge, guard.path, name, context)
21
+ }
22
+
23
+ exports.guards = guards
package/src/index.js CHANGED
@@ -14,6 +14,8 @@ const { operation } = require('./operation')
14
14
  const { receivers, receive } = require('./receivers')
15
15
  const { remote } = require('./remote')
16
16
  const { storage } = require('./storage')
17
+ const { guards } = require('./guards')
18
+ const { rc } = require('./rc')
17
19
 
18
20
  exports.bindings = require('./bindings')
19
21
  exports.bridge = require('./bridge')
@@ -34,3 +36,5 @@ exports.receivers = receivers
34
36
  exports.receive = receive
35
37
  exports.remote = remote
36
38
  exports.storage = storage
39
+ exports.guards = guards
40
+ exports.rc = rc
package/src/manifest.js CHANGED
@@ -1,17 +1,18 @@
1
1
  'use strict'
2
2
 
3
3
  const clone = require('clone-deep')
4
+ const { basename } = require('node:path')
4
5
  const { merge } = require('@toa.io/generic')
5
6
  const { component: load } = require('@toa.io/norm')
6
7
  const { Locator } = require('@toa.io/core')
7
8
 
8
- /**
9
- * @type {toa.boot.Manifest}
10
- */
9
+ const { span } = require('./span')
10
+
11
11
  const manifest = async (path, options = {}) => {
12
12
  options = merge(clone(options), DEFAULTS)
13
13
 
14
- const manifest = await load(path)
14
+ const manifest = await span({ name: `manifest ${basename(path)}`, attributes: { path } },
15
+ async () => await load(path))
15
16
 
16
17
  if (options?.bindings !== undefined) {
17
18
  if ('operations' in manifest) {
package/src/operation.js CHANGED
@@ -1,15 +1,24 @@
1
1
  'use strict'
2
2
 
3
- const { Transition, Observation, Assignment, Operation, Query } = require('@toa.io/core')
3
+ const {
4
+ Transition,
5
+ Observation,
6
+ Assignment,
7
+ Operation,
8
+ Query,
9
+ Effect,
10
+ Unmanaged
11
+ } = require('@toa.io/core')
4
12
 
5
13
  const boot = require('./index')
6
14
 
7
- const operation = (manifest, endpoint, definition, context, scope) => {
8
- const cascade = boot.cascade(manifest, endpoint, definition, context)
15
+ async function operation (manifest, endpoint, definition, context, scope, rc) {
16
+ const cascade = await boot.cascade(manifest, endpoint, definition, context, rc)
9
17
  const reply = boot.contract.reply(definition.output, definition.error)
10
18
  const input = definition.input
11
19
  const request = boot.contract.request({ input }, manifest.entity)
12
20
  const contracts = { reply, request }
21
+
13
22
  const query = manifest.entity === undefined
14
23
  ? undefined
15
24
  : new Query(manifest.entity.schema.properties)
@@ -24,7 +33,8 @@ const TYPES = {
24
33
  observation: Observation,
25
34
  assignment: Assignment,
26
35
  computation: Operation,
27
- effect: Operation
36
+ effect: Effect,
37
+ unmanaged: Unmanaged
28
38
  }
29
39
 
30
40
  exports.operation = operation
package/src/rc.js ADDED
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ const boot = require('./index')
4
+
5
+ async function rc (manifest, context) {
6
+ return boot.bridge.rc(manifest.bridge, manifest.path, context)
7
+ }
8
+
9
+ exports.rc = rc
package/src/receivers.js CHANGED
@@ -5,23 +5,25 @@ const { Receiver, Locator } = require('@toa.io/core')
5
5
  const boot = require('./index')
6
6
  const extensions = require('./extensions')
7
7
 
8
- const receivers = async (manifest, runtime) => {
8
+ const receivers = async (manifest, component) => {
9
9
  if (manifest.receivers === undefined) return []
10
10
 
11
11
  const receivers = []
12
12
  const local = await boot.remote(manifest.locator, manifest)
13
13
 
14
14
  for (const [label, definition] of Object.entries(manifest.receivers)) {
15
+ const locator = Locator.parse(label)
16
+ const source = definition.source ? Locator.parse(definition.source) : locator
17
+ const destination = `${source.id}.${label.split('.').pop()}`
18
+
15
19
  const bridge = definition.bridge !== undefined ? boot.bridge.receiver(definition.bridge, manifest.path, label) : undefined
16
- const receiver = new Receiver(definition, local, bridge)
20
+ const receiver = new Receiver({ ...definition, label, destination }, local, bridge)
17
21
  const decorator = extensions.receiver(receiver, manifest.locator)
18
22
 
19
- const locator = Locator.parse(label)
20
23
  const transport = definition.binding ?? await resolveBinding(locator, label)
21
- const source = definition.source ? Locator.parse(definition.source) : locator
22
24
  const binding = boot.bindings.receive(transport, source, label, manifest.locator.id, decorator)
23
25
 
24
- binding.depends(runtime)
26
+ binding.depends(component)
25
27
  receivers.push(binding)
26
28
  }
27
29
 
package/src/remote.js CHANGED
@@ -1,14 +1,20 @@
1
1
  'use strict'
2
2
 
3
+ const { console } = require('openspan')
3
4
  const { Remote } = require('@toa.io/core')
4
5
  const { remap } = require('@toa.io/generic')
5
6
 
6
7
  const boot = require('./index')
7
8
 
8
- const remote = async (locator, manifest = undefined) => {
9
- const discovery = await boot.discovery.discovery()
9
+ const remote = async (locator, manifest) => {
10
+ let discovery
10
11
 
11
- if (manifest === undefined) manifest = await discovery.lookup(locator)
12
+ if (manifest === undefined) {
13
+ console.debug('Lookup', { locator: locator.id })
14
+
15
+ discovery = await boot.discovery.discovery(locator)
16
+ manifest = await discovery.lookup(locator)
17
+ }
12
18
 
13
19
  const calls = manifest.operations === undefined
14
20
  ? {}
@@ -17,7 +23,8 @@ const remote = async (locator, manifest = undefined) => {
17
23
  const remote = new Remote(locator, calls)
18
24
 
19
25
  // ensure discovery shutdown
20
- remote.depends(discovery)
26
+ if (discovery !== undefined)
27
+ remote.depends(discovery)
21
28
 
22
29
  return remote
23
30
  }
package/src/span.js ADDED
@@ -0,0 +1,16 @@
1
+ 'use strict'
2
+
3
+ const { console } = require('openspan')
4
+
5
+ const enabled = process.env.TOA_BOOT_TRACE === '1'
6
+
7
+ /**
8
+ * Creates a span if boot tracing is enabled (TOA_BOOT_TRACE=1), otherwise just runs the task
9
+ *
10
+ * @param {string | object} options
11
+ * @param {() => Promise<any>} task
12
+ * @returns {Promise<any>}
13
+ */
14
+ const span = async (options, task) => enabled ? console.span(options, task) : task()
15
+
16
+ exports.span = span
package/types/index.d.ts CHANGED
@@ -5,17 +5,17 @@ export * as bindings from './bindings'
5
5
 
6
6
  export async function composition (paths: string[], options?: composition.Options): Promise<core.Connector>
7
7
 
8
- export async function remote (locator: core.Locator): Promise<core.Component>
8
+ export async function remote (locator: core.Locator): Promise<core.Remote>
9
9
 
10
10
  export async function receive<T = any> (
11
11
  label: string,
12
- receiver: Receiver,
12
+ receiver: Receiver
13
13
  ): Promise<core.Connector>
14
14
 
15
15
  export async function receive<T = any> (
16
16
  label: string,
17
17
  group: string | undefined,
18
- receiver: Receiver,
18
+ receiver: Receiver
19
19
  ): Promise<core.Connector>
20
20
 
21
21
  type Receiver = { receive: (message: core.Message<T>) => void | Promise<void> }