@toa.io/boot 1.0.0-alpha.311 → 1.0.0-alpha.313

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 CHANGED
@@ -3,6 +3,24 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.0.0-alpha.313](https://github.com/toa-io/toa/compare/v1.0.0-alpha.312...v1.0.0-alpha.313) (2026-09-18)
7
+
8
+ **Note:** Version bump only for package @toa.io/boot
9
+
10
+
11
+
12
+
13
+
14
+ # [1.0.0-alpha.312](https://github.com/toa-io/toa/compare/v1.0.0-alpha.311...v1.0.0-alpha.312) (2026-09-18)
15
+
16
+ ### Features
17
+
18
+ * **boot:** offer a streamed endpoint to the bindings that carry a stream ([0099b4e](https://github.com/toa-io/toa/commit/0099b4e12bff4ce5dba2d0098f0b8c6a18fff2c5))
19
+ * **boot:** tell a producer which of its endpoints take a stream ([6383adc](https://github.com/toa-io/toa/commit/6383adcebf38d889db80d8cbd19fe38f3ba4eb2e))
20
+ * **core:** refuse a call that carries no stream where the operation takes one ([fb78e02](https://github.com/toa-io/toa/commit/fb78e02fa620b15f6aa7bac184dd8a6ede923150))
21
+ * **core:** require the property that carries a stream, and refuse a task that carries one ([fdc7e6b](https://github.com/toa-io/toa/commit/fdc7e6b345ee22b030a52f1fda0e91e6bf63a392))
22
+
23
+
6
24
  # [1.0.0-alpha.311](https://github.com/toa-io/toa/compare/v1.0.0-alpha.310...v1.0.0-alpha.311) (2026-09-17)
7
25
 
8
26
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/boot",
3
- "version": "1.0.0-alpha.311",
3
+ "version": "1.0.0-alpha.313",
4
4
  "type": "module",
5
5
  "description": "Toa Boot",
6
6
  "author": "temich <tema.gurtovoy@gmail.com>",
@@ -21,15 +21,15 @@
21
21
  "test": "echo \"Error: run tests from root\" && exit 1"
22
22
  },
23
23
  "dependencies": {
24
- "@toa.io/atomicity": "1.0.0-alpha.311",
25
- "@toa.io/core": "1.0.0-alpha.311",
26
- "@toa.io/definitions": "1.0.0-alpha.311",
24
+ "@toa.io/atomicity": "1.0.0-alpha.312",
25
+ "@toa.io/core": "1.0.0-alpha.312",
26
+ "@toa.io/definitions": "1.0.0-alpha.313",
27
27
  "@toa.io/generic": "1.0.0-alpha.311",
28
- "@toa.io/norm": "1.0.0-alpha.311",
28
+ "@toa.io/norm": "1.0.0-alpha.313",
29
29
  "@toa.io/schemas": "1.0.0-alpha.311",
30
30
  "clone-deep": "4.0.1",
31
31
  "dotenv": "17.4.2",
32
32
  "openspan": "1.0.0-alpha.305"
33
33
  },
34
- "gitHead": "4f45061964e275296a27ff8ff39f757ce27d4008"
34
+ "gitHead": "7da3b2627ceeefd1fa19d5c8851be88d928523b6"
35
35
  }
@@ -1,9 +1,21 @@
1
1
  import { LOOP } from './constants.js'
2
2
  import { factory } from './factory.js'
3
+ import { carries } from './produce.js'
3
4
 
4
- export const consume = async (locator, endpoint, bindings) =>
5
- await Promise.all(
6
- [LOOP]
7
- .concat(bindings)
8
- .map(async (binding) => (await factory(binding)).consumer(locator, endpoint))
5
+ export const consume = async (locator, endpoint, bindings, streamed = false) => {
6
+ const declared = [LOOP].concat(bindings)
7
+ const carrying = streamed ? await filter(declared) : declared
8
+
9
+ return await Promise.all(
10
+ carrying.map(async (binding) => (await factory(binding)).consumer(locator, endpoint))
9
11
  )
12
+ }
13
+
14
+ /** A call that carries a stream is handed to nothing that cannot carry one. */
15
+ const filter = async (bindings) => {
16
+ const carrying = []
17
+
18
+ for (const binding of bindings) if (await carries(binding)) carrying.push(binding)
19
+
20
+ return carrying
21
+ }
@@ -6,11 +6,13 @@ export const produce = async (component, operations) => {
6
6
  const local = []
7
7
  const other = []
8
8
  const stateful = addressed(operations)
9
+ const streams = streamed(operations)
9
10
 
10
- for (const [binding, endpoints] of group(operations)) {
11
+ for (const [binding, endpoints] of await group(operations)) {
11
12
  const made = await factory(binding)
12
13
  const carried = stateful.filter((endpoint) => endpoints.includes(endpoint))
13
- const producer = made.producer(component.locator, endpoints, component, carried)
14
+ const carries = streams.filter((endpoint) => endpoints.includes(endpoint))
15
+ const producer = made.producer(component.locator, endpoints, component, carried, carries)
14
16
  const { properties } = (await definition(binding)).module
15
17
 
16
18
  if (properties.local === true) local.push(producer)
@@ -20,6 +22,14 @@ export const produce = async (component, operations) => {
20
22
  return { local, other }
21
23
  }
22
24
 
25
+ /** The endpoints that take one of their input properties as a stream. */
26
+ const streamed = (operations) =>
27
+ operations === undefined
28
+ ? []
29
+ : Object.entries(operations)
30
+ .filter(([, operation]) => operation.stream !== undefined)
31
+ .map(([endpoint]) => endpoint)
32
+
23
33
  /** The endpoints that take addressed calls only. */
24
34
  const addressed = (operations) =>
25
35
  operations === undefined
@@ -29,7 +39,7 @@ const addressed = (operations) =>
29
39
  .map(([endpoint]) => endpoint)
30
40
 
31
41
  /** The endpoints each binding carries, as entries. */
32
- const group = (operations) => {
42
+ const group = async (operations) => {
33
43
  const map = {}
34
44
 
35
45
  if (operations !== undefined)
@@ -40,6 +50,9 @@ const group = (operations) => {
40
50
  : [LOOP].concat(operation.bindings)
41
51
 
42
52
  for (const binding of bindings) {
53
+ // an endpoint that takes a stream is served by the bindings that carry one, and by no other
54
+ if (operation.stream !== undefined && !(await carries(binding))) continue
55
+
43
56
  if (!map[binding]) map[binding] = []
44
57
 
45
58
  map[binding].push(endpoint)
@@ -48,3 +61,7 @@ const group = (operations) => {
48
61
 
49
62
  return Object.entries(map)
50
63
  }
64
+
65
+ /** Whether a binding carries a call whose input holds a stream, which only its module says. */
66
+ export const carries = async (binding) =>
67
+ (await definition(binding)).module.properties.streams === true
package/src/call.js CHANGED
@@ -4,7 +4,12 @@ import * as boot from './index.js'
4
4
 
5
5
  // eslint-disable-next-line max-params
6
6
  export const call = async (locator, endpoint, definition, entity, source) => {
7
- const consumers = await boot.bindings.consume(locator, endpoint, definition.bindings)
7
+ const consumers = await boot.bindings.consume(
8
+ locator,
9
+ endpoint,
10
+ definition.bindings,
11
+ definition.stream !== undefined
12
+ )
8
13
  const transmission = new Transmission(consumers)
9
14
  const contract = boot.contract.request(definition, entity)
10
15
 
@@ -23,6 +28,7 @@ export const call = async (locator, endpoint, definition, entity, source) => {
23
28
  `${locator.id}.${endpoint}`,
24
29
  source,
25
30
  stateful,
26
- readable
31
+ readable,
32
+ definition.stream
27
33
  )
28
34
  }
package/src/operation.js CHANGED
@@ -21,7 +21,7 @@ export async function operation(
21
21
  const cascade = await boot.cascade(manifest, endpoint, definition, context, preflight)
22
22
  const reply = boot.contract.reply(definition.output, definition.errors)
23
23
  const input = definition.input
24
- const request = boot.contract.request({ input }, manifest.entity)
24
+ const request = boot.contract.request({ input, stream: definition.stream }, manifest.entity)
25
25
  const contracts = { reply, request }
26
26
 
27
27
  const query =