@toa.io/norm 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 +18 -0
- package/package.json +4 -4
- package/src/.component/schema.yaml +10 -0
- package/src/.component/validate.js +22 -0
- package/src/.context/validate.js +4 -1
- package/src/shortcuts.js +1 -0
- package/test/component/validate.test.js +39 -0
- package/types/component.d.ts +2 -0
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/norm
|
|
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
|
+
* **bindings.http:** carry a call that holds a stream ([c571793](https://github.com/toa-io/toa/commit/c571793b617e3d5ae1dce5b98b64d67d7d8224f6))
|
|
19
|
+
* **cli:** write the property that carries a stream into a component's types ([8eae8ce](https://github.com/toa-io/toa/commit/8eae8cea011c0be78b27ed1380a1a6f17c9479f8))
|
|
20
|
+
* **norm:** let an operation declare the input property that carries a stream ([85a8f1f](https://github.com/toa-io/toa/commit/85a8f1ff5b62f658e0527dcce7230d8388d28fe0))
|
|
21
|
+
* **operations:** refuse to render a chart for a context with no version ([d6cd083](https://github.com/toa-io/toa/commit/d6cd083bfe8d7da9f3d451c2c25d821eb41ce958))
|
|
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
|
**Note:** Version bump only for package @toa.io/norm
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/norm",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.313",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Toa declarations normalization and validation",
|
|
6
6
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@toa.io/core": "1.0.0-alpha.
|
|
25
|
-
"@toa.io/definitions": "1.0.0-alpha.
|
|
24
|
+
"@toa.io/core": "1.0.0-alpha.312",
|
|
25
|
+
"@toa.io/definitions": "1.0.0-alpha.313",
|
|
26
26
|
"@toa.io/generic": "1.0.0-alpha.311",
|
|
27
27
|
"@toa.io/prototype": "1.0.0-alpha.301",
|
|
28
28
|
"@toa.io/schemas": "1.0.0-alpha.311",
|
|
29
29
|
"fast-glob": "3.3.3"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "7da3b2627ceeefd1fa19d5c8851be88d928523b6"
|
|
32
32
|
}
|
|
@@ -161,6 +161,9 @@ properties:
|
|
|
161
161
|
stateful:
|
|
162
162
|
# served by every process under an address of its own, which a call names; see documentation/stateful.md
|
|
163
163
|
type: boolean
|
|
164
|
+
stream:
|
|
165
|
+
# the input property that carries a stream, read as its caller writes it; see documentation/streams.md
|
|
166
|
+
$ref: "#/definitions/name"
|
|
164
167
|
forward:
|
|
165
168
|
$ref: "#/definitions/name"
|
|
166
169
|
bridge:
|
|
@@ -280,6 +283,13 @@ properties:
|
|
|
280
283
|
properties:
|
|
281
284
|
scope:
|
|
282
285
|
enum: [object, objects, stream, none]
|
|
286
|
+
- if: # takes a stream
|
|
287
|
+
required: [stream]
|
|
288
|
+
then:
|
|
289
|
+
properties:
|
|
290
|
+
type:
|
|
291
|
+
# a transition may run its algorithm twice over one call, and a stream is read once
|
|
292
|
+
enum: [effect, computation, unmanaged]
|
|
283
293
|
additionalProperties: false
|
|
284
294
|
additionalProperties: false
|
|
285
295
|
|
|
@@ -16,6 +16,7 @@ export const validate = async (manifest) => {
|
|
|
16
16
|
if (error) throw error
|
|
17
17
|
|
|
18
18
|
if (manifest.entity !== undefined) entity(manifest)
|
|
19
|
+
if (manifest.operations !== undefined) await streams(manifest)
|
|
19
20
|
if (manifest.events !== undefined) await events(manifest)
|
|
20
21
|
if (manifest.receivers !== undefined) receivers(manifest)
|
|
21
22
|
}
|
|
@@ -33,6 +34,27 @@ const entity = (manifest) => {
|
|
|
33
34
|
throw new Error(`Entity blank names property '${name}', which is not defined`)
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
/**
|
|
38
|
+
* An operation that takes a stream is reachable over a binding that carries one. A binding that
|
|
39
|
+
* declares none is served in this process alone, which is a component saying so.
|
|
40
|
+
*/
|
|
41
|
+
const streams = async (manifest) => {
|
|
42
|
+
for (const [endpoint, operation] of Object.entries(manifest.operations)) {
|
|
43
|
+
if (operation.stream === undefined || operation.bindings.length === 0) continue
|
|
44
|
+
if (await carried(operation.bindings)) continue
|
|
45
|
+
|
|
46
|
+
throw new Error(`Operation '${endpoint}' takes a stream, which none of its bindings carries`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Whether any of these bindings carries a call whose input holds a stream. */
|
|
51
|
+
const carried = async (bindings) => {
|
|
52
|
+
for (const binding of bindings)
|
|
53
|
+
if ((await definition(binding)).module.properties.streams === true) return true
|
|
54
|
+
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
|
|
36
58
|
const events = async (manifest) => {
|
|
37
59
|
for (const [label, event] of Object.entries(manifest.events)) {
|
|
38
60
|
const { properties } = (await definition(event.binding)).module
|
package/src/.context/validate.js
CHANGED
|
@@ -9,5 +9,8 @@ const object = yaml.load(readFileSync(path, 'utf8'))
|
|
|
9
9
|
const schema = schemas.schema(object)
|
|
10
10
|
|
|
11
11
|
export const validate = (context) => {
|
|
12
|
-
schema.validate(context)
|
|
12
|
+
schema.validate(context, CONTEXT)
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
/** The file a reader has to edit, since nothing else in the message names it. */
|
|
16
|
+
const CONTEXT = 'context.toa.yaml'
|
package/src/shortcuts.js
CHANGED
|
@@ -38,6 +38,7 @@ export const recognize = (shortcuts, object, group) => {
|
|
|
38
38
|
|
|
39
39
|
export const SHORTCUTS = {
|
|
40
40
|
amqp: '@toa.io/bindings.amqp',
|
|
41
|
+
http: '@toa.io/bindings.http',
|
|
41
42
|
node: '@toa.io/bridges.node',
|
|
42
43
|
bash: '@toa.io/bridges.bash',
|
|
43
44
|
mongodb: '@toa.io/storages.mongodb',
|
|
@@ -330,3 +330,42 @@ describe('stateful', () => {
|
|
|
330
330
|
await assert.rejects(validate(manifest), (error) => /must be boolean/.test(error.message))
|
|
331
331
|
})
|
|
332
332
|
})
|
|
333
|
+
|
|
334
|
+
describe('stream', () => {
|
|
335
|
+
beforeEach(() => {
|
|
336
|
+
manifest.operations.read = {
|
|
337
|
+
type: 'computation',
|
|
338
|
+
scope: 'none',
|
|
339
|
+
query: false,
|
|
340
|
+
bridge: 'whatever',
|
|
341
|
+
bindings: ['@toa.io/bindings.amqp'],
|
|
342
|
+
stream: 'content'
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
it('should reject an operation whose bindings carry no stream', async () => {
|
|
347
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
348
|
+
/takes a stream, which none of its bindings carries/.test(error.message)
|
|
349
|
+
)
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('should accept an operation served where it is composed', async () => {
|
|
353
|
+
manifest.operations.read.bindings = []
|
|
354
|
+
|
|
355
|
+
await assert.doesNotReject(validate(manifest))
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
it('should accept an operation bound to a binding that carries a stream', async () => {
|
|
359
|
+
manifest.operations.read.bindings = ['@toa.io/bindings.http']
|
|
360
|
+
|
|
361
|
+
await assert.doesNotReject(validate(manifest))
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('should reject a transition that takes a stream', async () => {
|
|
365
|
+
manifest.operations.add.stream = 'content'
|
|
366
|
+
|
|
367
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
368
|
+
/must be equal to one of the allowed values/.test(error.message)
|
|
369
|
+
)
|
|
370
|
+
})
|
|
371
|
+
})
|
package/types/component.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export type Operation = {
|
|
|
16
16
|
query?: boolean
|
|
17
17
|
/** served by every process under an address of its own, which a call to it names */
|
|
18
18
|
stateful?: boolean
|
|
19
|
+
/** the input property that carries a stream, read as its caller writes it */
|
|
20
|
+
stream?: string
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export type Operations = Record<string, Operation>
|