@toa.io/boot 1.0.0-alpha.29 → 1.0.0-alpha.292

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/package.json +11 -8
  3. package/src/atom.js +41 -0
  4. package/src/atomicity.js +11 -0
  5. package/src/bindings/broadcast.js +3 -6
  6. package/src/bindings/constants.js +1 -3
  7. package/src/bindings/consume.js +8 -8
  8. package/src/bindings/emit.js +3 -6
  9. package/src/bindings/factory.js +5 -9
  10. package/src/bindings/index.js +5 -13
  11. package/src/bindings/produce.js +30 -18
  12. package/src/bindings/receive.js +3 -6
  13. package/src/bridge.js +22 -16
  14. package/src/call.js +6 -9
  15. package/src/cascade.js +16 -10
  16. package/src/component.js +79 -32
  17. package/src/composition.js +104 -15
  18. package/src/context.js +7 -8
  19. package/src/contract.js +11 -12
  20. package/src/discovery.js +29 -18
  21. package/src/emission.js +21 -14
  22. package/src/env.js +19 -12
  23. package/src/events.js +27 -0
  24. package/src/extensions/aspects.js +4 -8
  25. package/src/extensions/component.js +4 -8
  26. package/src/extensions/context.js +4 -8
  27. package/src/extensions/emitter.js +7 -9
  28. package/src/extensions/index.js +9 -19
  29. package/src/extensions/instances.js +2 -4
  30. package/src/extensions/load.js +7 -7
  31. package/src/extensions/manage.js +15 -0
  32. package/src/extensions/receiver.js +5 -9
  33. package/src/extensions/resolve.js +36 -11
  34. package/src/extensions/storage.js +4 -8
  35. package/src/extensions/tenants.js +5 -9
  36. package/src/guards.js +20 -0
  37. package/src/host.js +15 -0
  38. package/src/index.js +48 -32
  39. package/src/manifest.js +25 -16
  40. package/src/manifest.test.js +12 -8
  41. package/src/operation.js +25 -13
  42. package/src/outbox.js +17 -0
  43. package/src/rc.js +5 -0
  44. package/src/receivers.js +35 -21
  45. package/src/remote.js +38 -17
  46. package/src/span.js +19 -0
  47. package/src/storage.js +35 -14
  48. package/src/storage.test.js +57 -0
  49. package/types/bindings.d.ts +6 -2
  50. package/types/index.d.ts +19 -10
  51. package/types/manifest.d.ts +5 -4
  52. package/src/exposition.js +0 -17
@@ -1,12 +1,10 @@
1
- 'use strict'
2
-
3
- const { instances } = require('./instances')
1
+ import { instances } from './instances.js'
4
2
 
5
3
  /**
6
- * @param {toa.core.Storage} storage
7
- * @returns {toa.core.Storage}
4
+ * @param {import('@toa.io/core/types').storages.Storage} storage
5
+ * @returns {import('@toa.io/core/types').storages.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
- 'use strict'
2
-
3
- const { resolve } = require('./resolve')
1
+ import { resolve } from './resolve.js'
4
2
 
5
3
  /**
6
4
  * @param {toa.norm.Component} manifest
7
- * @returns {toa.core.Connector[]}
5
+ * @returns {import('@toa.io/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
package/src/guards.js ADDED
@@ -0,0 +1,20 @@
1
+ import { Guard } from '@toa.io/core'
2
+ import * as boot from './index.js'
3
+
4
+ export async function guards(manifest, context) {
5
+ if (manifest.guards === undefined) return
6
+
7
+ const entries = Object.entries(manifest.guards)
8
+
9
+ return await Promise.all(
10
+ entries.map(async ([name, guard]) => {
11
+ const bridge = await load(guard, name, context)
12
+
13
+ return new Guard(name, bridge)
14
+ })
15
+ )
16
+ }
17
+
18
+ async function load(guard, name, context) {
19
+ return await boot.bridge.guard(guard.bridge, guard.path, name, context)
20
+ }
package/src/host.js ADDED
@@ -0,0 +1,15 @@
1
+ import * as boot from './index.js'
2
+
3
+ /**
4
+ * What the process hosting an extension provides to it. The counterpart of a component's
5
+ * context: an extension reaches the core through this and through nothing else.
6
+ *
7
+ * @returns {import('@toa.io/core/types').extensions.Host}
8
+ */
9
+ export const host = () => ({
10
+ remote: boot.remote,
11
+ broadcast: boot.bindings.broadcast,
12
+ composition: boot.composition,
13
+ receive: boot.receive,
14
+ atom: boot.atomicity
15
+ })
package/src/index.js CHANGED
@@ -1,36 +1,52 @@
1
- 'use strict'
1
+ // the environment is read at module scope elsewhere, so it is loaded first
2
+ import { load as env } from './env.js'
2
3
 
3
- require('./env')
4
+ import * as bindings from './bindings/index.js'
5
+ import * as bridge from './bridge.js'
6
+ import * as contract from './contract.js'
7
+ import * as discovery from './discovery.js'
8
+ import * as extensions from './extensions/index.js'
4
9
 
5
- const { call } = require('./call')
6
- const { cascade } = require('./cascade')
7
- const { component } = require('./component')
8
- const { composition } = require('./composition')
9
- const { context } = require('./context')
10
- const { emission } = require('./emission')
11
- const { exposition } = require('./exposition')
12
- const { manifest } = require('./manifest')
13
- const { operation } = require('./operation')
14
- const { receivers, receive } = require('./receivers')
15
- const { remote } = require('./remote')
16
- const { storage } = require('./storage')
10
+ import { call } from './call.js'
11
+ import { cascade } from './cascade.js'
12
+ import { component } from './component.js'
13
+ import { composition } from './composition.js'
14
+ import { context } from './context.js'
15
+ import { emission } from './emission.js'
16
+ import { events } from './events.js'
17
+ import { outbox } from './outbox.js'
18
+ import { atomicity } from './atomicity.js'
19
+ import { atom } from './atom.js'
20
+ import { manifest } from './manifest.js'
21
+ import { operation } from './operation.js'
22
+ import { receivers, receive } from './receivers.js'
23
+ import { remote } from './remote.js'
24
+ import { storage } from './storage.js'
25
+ import { guards } from './guards.js'
26
+ import { host } from './host.js'
27
+ import { rc } from './rc.js'
17
28
 
18
- exports.bindings = require('./bindings')
19
- exports.bridge = require('./bridge')
20
- exports.contract = require('./contract')
21
- exports.discovery = require('./discovery')
22
- exports.extensions = require('./extensions')
29
+ export {
30
+ call,
31
+ env,
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
+ }
23
51
 
24
- exports.call = call
25
- exports.cascade = cascade
26
- exports.component = component
27
- exports.composition = composition
28
- exports.context = context
29
- exports.emission = emission
30
- exports.exposition = exposition
31
- exports.manifest = manifest
32
- exports.operation = operation
33
- exports.receivers = receivers
34
- exports.receive = receive
35
- exports.remote = remote
36
- exports.storage = storage
52
+ export { bindings, bridge, contract, discovery, extensions }
package/src/manifest.js CHANGED
@@ -1,17 +1,18 @@
1
- 'use strict'
1
+ import clone from 'clone-deep'
2
+ import { basename } from 'node:path'
3
+ import { merge } from '@toa.io/generic'
4
+ import { component as load, definition } from '@toa.io/norm'
5
+ import { Locator } from '@toa.io/core'
2
6
 
3
- const clone = require('clone-deep')
4
- const { merge } = require('@toa.io/generic')
5
- const { component: load } = require('@toa.io/norm')
6
- const { Locator } = require('@toa.io/core')
7
+ import { span } from './span.js'
7
8
 
8
- /**
9
- * @type {toa.boot.Manifest}
10
- */
11
- const manifest = async (path, options = {}) => {
9
+ export const manifest = async (path, options = {}) => {
12
10
  options = merge(clone(options), DEFAULTS)
13
11
 
14
- const manifest = await load(path)
12
+ const manifest = await span(
13
+ { name: `manifest ${basename(path)}`, attributes: { path } },
14
+ async () => await load(path)
15
+ )
15
16
 
16
17
  if (options?.bindings !== undefined) {
17
18
  if ('operations' in manifest) {
@@ -20,10 +21,19 @@ const manifest = async (path, options = {}) => {
20
21
  }
21
22
  }
22
23
 
23
- const check = (binding) => require(binding).properties?.async === true
24
- const asyncBinding = options.bindings.find(check)
24
+ let asyncBinding
25
25
 
26
- if (asyncBinding === undefined) throw new Error('Bindings override must contain at least one async binding')
26
+ for (const binding of options.bindings) {
27
+ const { properties } = (await definition(binding)).module
28
+
29
+ if (properties?.async === true) {
30
+ asyncBinding = binding
31
+ break
32
+ }
33
+ }
34
+
35
+ if (asyncBinding === undefined)
36
+ throw new Error('Bindings override must contain at least one async binding')
27
37
 
28
38
  if ('events' in manifest) {
29
39
  for (const event of Object.values(manifest.events)) event.binding = asyncBinding
@@ -44,7 +54,8 @@ const manifest = async (path, options = {}) => {
44
54
  }
45
55
  }
46
56
 
47
- if ('storage' in options && 'entity' in manifest) manifest.entity.storage = options.storage
57
+ if ('storage' in options && 'entity' in manifest)
58
+ manifest.entity.storage = options.storage
48
59
 
49
60
  manifest.locator = new Locator(manifest.name, manifest.namespace)
50
61
 
@@ -52,5 +63,3 @@ const manifest = async (path, options = {}) => {
52
63
  }
53
64
 
54
65
  const DEFAULTS = {}
55
-
56
- exports.manifest = manifest
@@ -1,12 +1,16 @@
1
- 'use strict'
1
+ import { it, mock } from 'node:test'
2
+ import assert from 'node:assert/strict'
2
3
 
3
- const { generate } = require('randomstring')
4
+ import { generate } from 'randomstring'
4
5
 
5
- jest.mock('@toa.io/norm', () => ({
6
- component: () => mockComponent()
7
- }))
6
+ mock.module('@toa.io/norm', {
7
+ namedExports: {
8
+ component: () => mockComponent(),
9
+ definition: async () => ({ module: {} })
10
+ }
11
+ })
8
12
 
9
- const { manifest } = require('./manifest')
13
+ const { manifest } = await import('./manifest.js')
10
14
 
11
15
  const path = generate()
12
16
 
@@ -15,9 +19,9 @@ it('should not modify options', async () => {
15
19
 
16
20
  await manifest(path, options)
17
21
 
18
- expect(options.extensions.length).toStrictEqual(2)
22
+ assert.deepStrictEqual(options.extensions.length, 2)
19
23
  })
20
24
 
21
- function mockComponent () {
25
+ function mockComponent() {
22
26
  return { name: generate(), namespace: generate() }
23
27
  }
package/src/operation.js CHANGED
@@ -1,18 +1,31 @@
1
- 'use strict'
1
+ import {
2
+ Transition,
3
+ Observation,
4
+ Assignment,
5
+ Operation,
6
+ Query,
7
+ Effect,
8
+ Unmanaged
9
+ } from '@toa.io/core'
2
10
 
3
- const { Transition, Observation, Assignment, Operation, Query } = require('@toa.io/core')
11
+ import * as boot from './index.js'
4
12
 
5
- const boot = require('./index')
6
-
7
- const operation = (manifest, endpoint, definition, context, scope) => {
8
- const cascade = boot.cascade(manifest, endpoint, definition, context)
9
- const reply = boot.contract.reply(definition.output, definition.error)
13
+ export async function operation(
14
+ manifest,
15
+ endpoint,
16
+ definition,
17
+ context,
18
+ scope,
19
+ preflight
20
+ ) {
21
+ const cascade = await boot.cascade(manifest, endpoint, definition, context, preflight)
22
+ const reply = boot.contract.reply(definition.output, definition.errors)
10
23
  const input = definition.input
11
24
  const request = boot.contract.request({ input }, manifest.entity)
12
25
  const contracts = { reply, request }
13
- const query = manifest.entity === undefined
14
- ? undefined
15
- : new Query(manifest.entity.schema.properties)
26
+
27
+ const query =
28
+ manifest.entity === undefined ? undefined : new Query(manifest.entity.properties)
16
29
 
17
30
  const Type = TYPES[definition.type]
18
31
 
@@ -24,7 +37,6 @@ const TYPES = {
24
37
  observation: Observation,
25
38
  assignment: Assignment,
26
39
  computation: Operation,
27
- effect: Operation
40
+ effect: Effect,
41
+ unmanaged: Unmanaged
28
42
  }
29
-
30
- exports.operation = operation
package/src/outbox.js ADDED
@@ -0,0 +1,17 @@
1
+ import { Outbox } from '@toa.io/core'
2
+
3
+ import * as boot from './index.js'
4
+
5
+ /**
6
+ * A component with no declared events has nothing to publish and therefore no outbox — the
7
+ * static check that keeps the common case free of a transaction.
8
+ *
9
+ * @param {toa.norm.Component} manifest
10
+ * @param {import('@toa.io/core/types').storages.Storage} [storage]
11
+ * @param {import('@toa.io/core').Emission} [emission]
12
+ */
13
+ export const outbox = (manifest, storage, emission) => {
14
+ if (emission === undefined) return
15
+
16
+ return new Outbox(emission, storage, boot.atomicity(manifest.locator.id), {})
17
+ }
package/src/rc.js ADDED
@@ -0,0 +1,5 @@
1
+ import * as boot from './index.js'
2
+
3
+ export async function rc(manifest, context) {
4
+ return boot.bridge.rc(manifest.bridge, manifest.path, context)
5
+ }
package/src/receivers.js CHANGED
@@ -1,34 +1,51 @@
1
- 'use strict'
1
+ import { Receiver, Locator } from '@toa.io/core'
2
2
 
3
- const { Receiver, Locator } = require('@toa.io/core')
3
+ import * as boot from './index.js'
4
+ import * as extensions from './extensions/index.js'
4
5
 
5
- const boot = require('./index')
6
- const extensions = require('./extensions')
7
-
8
- const receivers = async (manifest, runtime) => {
6
+ export const receivers = async (manifest, component) => {
9
7
  if (manifest.receivers === undefined) return []
10
8
 
11
9
  const receivers = []
12
- const local = await boot.remote(manifest.locator, manifest)
10
+ const local = await boot.remote(manifest.locator, undefined, manifest)
13
11
 
14
12
  for (const [label, definition] of Object.entries(manifest.receivers)) {
15
- const bridge = definition.bridge !== undefined ? boot.bridge.receiver(definition.bridge, manifest.path, label) : undefined
16
- const receiver = new Receiver(definition, local, bridge)
17
- const decorator = extensions.receiver(receiver, manifest.locator)
18
-
19
13
  const locator = Locator.parse(label)
20
- const transport = definition.binding ?? await resolveBinding(locator, label)
21
14
  const source = definition.source ? Locator.parse(definition.source) : locator
22
- const binding = boot.bindings.receive(transport, source, label, manifest.locator.id, decorator)
15
+ const event = label.split('.').pop()
16
+ const destination = `${source.id}.${event}`
17
+
18
+ // the origin of the calls this receiver makes to its local operation
19
+ const origin = { namespace: source.namespace, component: source.name, event }
23
20
 
24
- binding.depends(runtime)
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
+ )
30
+ const decorator = extensions.receiver(receiver, manifest.locator)
31
+
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
+ )
40
+
41
+ binding.depends(component)
25
42
  receivers.push(binding)
26
43
  }
27
44
 
28
45
  return receivers
29
46
  }
30
47
 
31
- async function receive (label, group, callback) {
48
+ export async function receive(label, group, callback) {
32
49
  if (callback === undefined) {
33
50
  callback = group
34
51
  group = undefined
@@ -37,21 +54,18 @@ async function receive (label, group, callback) {
37
54
  const locator = Locator.parse(label)
38
55
  const transport = await resolveBinding(locator, label)
39
56
 
40
- return boot.bindings.receive(transport, locator, label, group, callback)
57
+ return await boot.bindings.receive(transport, locator, label, group, callback)
41
58
  }
42
59
 
43
60
  /**
44
- * @param {toa.core.Locator} locator
61
+ * @param {import('@toa.io/core').Locator} locator
45
62
  * @param {string} label
46
63
  * @return {Promise<string>}
47
64
  */
48
- async function resolveBinding (locator, label) {
65
+ async function resolveBinding(locator, label) {
49
66
  const event = label.split('.').pop()
50
67
  const discovery = await boot.discovery.discovery()
51
68
  const { events } = await discovery.lookup(locator)
52
69
 
53
70
  return events[event].binding
54
71
  }
55
-
56
- exports.receivers = receivers
57
- exports.receive = receive
package/src/remote.js CHANGED
@@ -1,25 +1,46 @@
1
- 'use strict'
2
-
3
- const { Remote } = require('@toa.io/core')
4
- const { remap } = require('@toa.io/generic')
5
-
6
- const boot = require('./index')
7
-
8
- const remote = async (locator, manifest = undefined) => {
9
- const discovery = await boot.discovery.discovery()
10
-
11
- if (manifest === undefined) manifest = await discovery.lookup(locator)
12
-
13
- const calls = manifest.operations === undefined
14
- ? {}
15
- : remap(manifest.operations, (definition, endpoint) => boot.call(locator, endpoint, definition, manifest.entity))
1
+ import { console } from 'openspan'
2
+ import { Remote } from '@toa.io/core'
3
+ import { remap } from '@toa.io/generic'
4
+
5
+ import * as boot from './index.js'
6
+
7
+ /**
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
+ * @param {toa.norm.Component} [manifest] skips discovery when the manifest is already known
11
+ */
12
+ export const remote = async (locator, source, manifest) => {
13
+ let discovery
14
+
15
+ if (manifest === undefined) {
16
+ console.debug('Lookup', { locator: locator.id })
17
+
18
+ discovery = await boot.discovery.discovery(locator)
19
+ manifest = await discovery.lookup(locator)
20
+ }
21
+
22
+ // a call binds its consumers, which are loaded rather than required
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
+ )
16
31
 
17
32
  const remote = new Remote(locator, calls)
18
33
 
19
34
  // ensure discovery shutdown
20
- remote.depends(discovery)
35
+ if (discovery !== undefined) remote.depends(discovery)
21
36
 
22
37
  return remote
23
38
  }
24
39
 
25
- exports.remote = remote
40
+ /** An object whose values are promises, as an object of what they resolve to. */
41
+ async function settle(object) {
42
+ const entries = Object.entries(object)
43
+ const values = await Promise.all(entries.map(([, promise]) => promise))
44
+
45
+ return Object.fromEntries(entries.map(([key], index) => [key, values[index]]))
46
+ }
package/src/span.js ADDED
@@ -0,0 +1,19 @@
1
+ import { console, traces } from 'openspan'
2
+ import { environment } from '@toa.io/generic'
3
+
4
+ const enabled = environment.get('TOA_BOOT_TRACE') === '1'
5
+
6
+ // Tracing is off until an exporter is configured, and the CLI opens its own span before
7
+ // any extension gets to configure one — so asking for a boot trace turns the console
8
+ // exporter on here. Extensions keep it: see `development()` in the telemetry factory.
9
+ if (enabled) traces({ exporters: { console: {} } })
10
+
11
+ /**
12
+ * Creates a span if boot tracing is enabled (TOA_BOOT_TRACE=1), otherwise just runs the task
13
+ *
14
+ * @param {string | object} options
15
+ * @param {() => Promise<any>} task
16
+ * @returns {Promise<any>}
17
+ */
18
+ export const span = async (options, task) =>
19
+ enabled ? console.span(options, task) : task()
package/src/storage.js CHANGED
@@ -1,26 +1,47 @@
1
- 'use strict'
2
-
3
- const { join } = require('node:path')
4
- const extensions = require('./extensions')
5
-
6
- const storage = (manifest) => {
1
+ import { createRequire } from 'node:module'
2
+ import { pathToFileURL } from 'node:url'
3
+ import * as extensions from './extensions/index.js'
4
+
5
+ // import.meta.resolve takes no paths, and the connector is resolved against the
6
+ // component that names it
7
+ const require = createRequire(import.meta.url)
8
+
9
+ /**
10
+ * @param {toa.norm.Component} manifest
11
+ * @param {boolean} outbox whether this component publishes anything, and so needs a place to
12
+ * commit it with the entity
13
+ */
14
+ export const storage = async (manifest, outbox) => {
7
15
  if (manifest.entity === undefined) return
8
16
 
9
- const Factory = load(manifest)
17
+ const Factory = await load(manifest)
10
18
 
11
- /** @type {toa.core.storages.Factory} */
19
+ /** @type {import('@toa.io/core/types').storages.Factory} */
12
20
  const factory = new Factory()
13
- const storage = factory.storage(manifest.locator, manifest.entity)
21
+ const storage = factory.storage(manifest.locator, manifest.entity, { outbox })
22
+
23
+ // a component whose structure nothing will make must not start with the structure it lacks;
24
+ // what it inherits was written for descendants that store, and one that stores nothing has
25
+ // nothing to convert
26
+ const own = manifest.entity.migrations?.some(
27
+ (migration) => migration.prototype === undefined
28
+ )
29
+
30
+ if (own === true && storage.migrates !== true)
31
+ throw new Error(
32
+ `Component '${manifest.locator.id}' declares migrations, ` +
33
+ `which storage '${manifest.entity.storage}' does not apply`
34
+ )
14
35
 
15
36
  return extensions.storage(storage)
16
37
  }
17
38
 
18
- function load (component) {
39
+ async function load(component) {
19
40
  const reference = component.entity.storage
20
- const path = require.resolve(reference, { paths: [component.path, __dirname] })
21
- const { Factory } = require(path)
41
+ const path = require.resolve(reference, {
42
+ paths: [component.path, import.meta.dirname]
43
+ })
44
+ const { Factory } = await import(pathToFileURL(path).href)
22
45
 
23
46
  return Factory
24
47
  }
25
-
26
- exports.storage = storage
@@ -0,0 +1,57 @@
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 inherited migrations a storage does not apply', async () => {
34
+ const entity = {
35
+ schema,
36
+ storage: '@toa.io/storages.null',
37
+ migrations: [{ id: 'system:0001', steps: [], prototype: 'system' }]
38
+ }
39
+
40
+ assert.notEqual(await storage(manifest(entity), false), undefined)
41
+ })
42
+
43
+ it('should accept a storage with no migrations', async () => {
44
+ const entity = { schema, storage: '@toa.io/storages.null' }
45
+
46
+ assert.notEqual(await storage(manifest(entity), false), undefined)
47
+ })
48
+
49
+ it('should accept migrations a storage applies', async () => {
50
+ const entity = {
51
+ schema,
52
+ storage: '@toa.io/storages.mongodb',
53
+ migrations: [{ id: '0001', steps: [] }]
54
+ }
55
+
56
+ assert.notEqual(await storage(manifest(entity), false), undefined)
57
+ })
@@ -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> (name: string, group?: string, binding?: string): bindings.Broadcast<T>
3
+ export function broadcast<T>(
4
+ name: string,
5
+ group?: string,
6
+ binding?: string
7
+ ): bindings.Broadcast<T>