@toa.io/boot 1.0.0-alpha.310 → 1.0.0-alpha.312

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,23 @@
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.312](https://github.com/toa-io/toa/compare/v1.0.0-alpha.311...v1.0.0-alpha.312) (2026-09-18)
7
+
8
+ ### Features
9
+
10
+ * **boot:** offer a streamed endpoint to the bindings that carry a stream ([0099b4e](https://github.com/toa-io/toa/commit/0099b4e12bff4ce5dba2d0098f0b8c6a18fff2c5))
11
+ * **boot:** tell a producer which of its endpoints take a stream ([6383adc](https://github.com/toa-io/toa/commit/6383adcebf38d889db80d8cbd19fe38f3ba4eb2e))
12
+ * **core:** refuse a call that carries no stream where the operation takes one ([fb78e02](https://github.com/toa-io/toa/commit/fb78e02fa620b15f6aa7bac184dd8a6ede923150))
13
+ * **core:** require the property that carries a stream, and refuse a task that carries one ([fdc7e6b](https://github.com/toa-io/toa/commit/fdc7e6b345ee22b030a52f1fda0e91e6bf63a392))
14
+
15
+
16
+ # [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)
17
+
18
+ ### Features
19
+
20
+ * read the scope of a process once, as it boots ([0d65de5](https://github.com/toa-io/toa/commit/0d65de58ffc655ff872b8e66f95c5609ada3370a))
21
+
22
+
6
23
  # [1.0.0-alpha.310](https://github.com/toa-io/toa/compare/v1.0.0-alpha.309...v1.0.0-alpha.310) (2026-09-16)
7
24
 
8
25
  **Note:** Version bump only for package @toa.io/boot
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/boot",
3
- "version": "1.0.0-alpha.310",
3
+ "version": "1.0.0-alpha.312",
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.310",
25
- "@toa.io/core": "1.0.0-alpha.310",
26
- "@toa.io/definitions": "1.0.0-alpha.310",
27
- "@toa.io/generic": "1.0.0-alpha.310",
28
- "@toa.io/norm": "1.0.0-alpha.310",
29
- "@toa.io/schemas": "1.0.0-alpha.310",
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.312",
27
+ "@toa.io/generic": "1.0.0-alpha.311",
28
+ "@toa.io/norm": "1.0.0-alpha.312",
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": "d952c57f77279729e26d52d50c6df47138ad0a4f"
34
+ "gitHead": "3cdff9b9779eeb6ae83913fefc0b502a10a0236a"
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/component.js CHANGED
@@ -17,6 +17,10 @@ const create = async (manifest, locator) => {
17
17
  // component is imported: nothing of it is left in `process.env` for that module to read
18
18
  environment.absorb()
19
19
 
20
+ // the suffix is what it is now, `.env` having been read: whatever this process sets for the
21
+ // processes it starts later does not rename what it has
22
+ environment.suffix()
23
+
20
24
  await boot.extensions.load(manifest)
21
25
 
22
26
  // the storage is told whether there will be an outbox, so what it publishes to comes first:
@@ -11,6 +11,10 @@ export async function composition(paths, options) {
11
11
  // component is imported: nothing of it is left in `process.env` for that module to read
12
12
  environment.absorb()
13
13
 
14
+ // the suffix is what it is now, `.env` having been read: whatever this process sets for the
15
+ // processes it starts later does not rename what it has
16
+ environment.suffix()
17
+
14
18
  options = Object.assign({}, options)
15
19
 
16
20
  return span('boot composition', async () => {
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 =