@toa.io/norm 1.0.0-alpha.300 → 1.0.0-alpha.302
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 +20 -0
- package/package.json +7 -7
- package/src/.component/.normalize/operations.js +3 -0
- package/src/.component/dereference.js +1 -47
- package/src/.component/merge.js +4 -6
- package/src/context.js +14 -4
- package/test/component/dereference.fixtures.js +2 -81
- package/test/component/dereference.test.js +0 -14
- package/test/component/normalize.test.js +8 -0
- package/types/context/declaration.d.ts +1 -1
- package/types/context.d.ts +10 -3
- package/types/entity.d.ts +1 -1
- package/types/index.d.ts +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
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.302](https://github.com/toa-io/toa/compare/v1.0.0-alpha.301...v1.0.0-alpha.302) (2026-09-11)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @toa.io/norm
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [1.0.0-alpha.301](https://github.com/toa-io/toa/compare/v1.0.0-alpha.300...v1.0.0-alpha.301) (2026-09-11)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **norm:** an operation that takes no input states none ([3b69b4c](https://github.com/toa-io/toa/commit/3b69b4c92b3f06361a3552f036a927f4ff32e58b))
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* **norm:** an environment name may fall back along a colon-separated chain ([a267c1c](https://github.com/toa-io/toa/commit/a267c1caae816a75ada8352d5361b99817c72eb1))
|
|
23
|
+
* **operations:** an evicted component is given its environment and configuration ([6cf7890](https://github.com/toa-io/toa/commit/6cf7890a632a2185d2b7d9790890a04187e5a897))
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
# [1.0.0-alpha.300](https://github.com/toa-io/toa/compare/v1.0.0-alpha.299...v1.0.0-alpha.300) (2026-09-11)
|
|
7
27
|
|
|
8
28
|
* feat(boot)!: a process is a thing, and an extension may keep something in one ([528d6cd](https://github.com/toa-io/toa/commit/528d6cdd307b45a6c30b6a184d7e06b3328cecf8))
|
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.302",
|
|
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.
|
|
26
|
-
"@toa.io/generic": "1.0.0-alpha.
|
|
27
|
-
"@toa.io/prototype": "1.0.0-alpha.
|
|
28
|
-
"@toa.io/schemas": "1.0.0-alpha.
|
|
24
|
+
"@toa.io/core": "1.0.0-alpha.302",
|
|
25
|
+
"@toa.io/definitions": "1.0.0-alpha.302",
|
|
26
|
+
"@toa.io/generic": "1.0.0-alpha.301",
|
|
27
|
+
"@toa.io/prototype": "1.0.0-alpha.301",
|
|
28
|
+
"@toa.io/schemas": "1.0.0-alpha.301",
|
|
29
29
|
"fast-glob": "3.3.3"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "3cbbca55a61711db78f98b13e50cf4d1afaf5c08"
|
|
32
32
|
}
|
|
@@ -13,6 +13,9 @@ export const operations = (component) => {
|
|
|
13
13
|
// an operation that states no output states an empty schema, which every reply fits
|
|
14
14
|
operation.output ??= {}
|
|
15
15
|
|
|
16
|
+
// a bridge says an algorithm takes no input with `null`, which the manifest states as none
|
|
17
|
+
if (operation.input === null) delete operation.input
|
|
18
|
+
|
|
16
19
|
if (operation.bindings === undefined) operation.bindings = component.bindings
|
|
17
20
|
if (operation.bindings === null) operation.bindings = []
|
|
18
21
|
if (operation.virtual === true) delete component.operations[endpoint]
|
|
@@ -1,36 +1,8 @@
|
|
|
1
1
|
import { merge } from '@toa.io/generic'
|
|
2
2
|
|
|
3
3
|
export const dereference = (manifest) => {
|
|
4
|
-
|
|
5
|
-
const properties = manifest.entity?.properties
|
|
6
|
-
const resolver = createResolver(properties)
|
|
4
|
+
if (!('operations' in manifest)) return
|
|
7
5
|
|
|
8
|
-
if (properties !== undefined)
|
|
9
|
-
for (const [name, property] of Object.entries(properties))
|
|
10
|
-
properties[name] = schema(property, resolver)
|
|
11
|
-
|
|
12
|
-
if ('operations' in manifest) operations(manifest, resolver)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const createResolver = (properties) => (property) => {
|
|
16
|
-
if (properties?.[property] === undefined) {
|
|
17
|
-
throw new Error(`Referenced property '${property}' is not defined`)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return properties[property]
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function operations(manifest, resolver) {
|
|
24
|
-
for (const operation of Object.values(manifest.operations)) {
|
|
25
|
-
if (operation.input !== undefined) operation.input = schema(operation.input, resolver)
|
|
26
|
-
|
|
27
|
-
if (operation.output !== undefined)
|
|
28
|
-
if (Array.isArray(operation.output) && operation.output.length === 1)
|
|
29
|
-
operation.output = [schema(operation.output[0], resolver)]
|
|
30
|
-
else operation.output = schema(operation.output, resolver)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// forwarding
|
|
34
6
|
for (const operation of Object.values(manifest.operations)) {
|
|
35
7
|
if (operation.forward !== undefined) forward(operation, manifest.operations)
|
|
36
8
|
}
|
|
@@ -40,24 +12,6 @@ function operations(manifest, resolver) {
|
|
|
40
12
|
}
|
|
41
13
|
}
|
|
42
14
|
|
|
43
|
-
const schema = (object, resolve) => {
|
|
44
|
-
if (object === undefined || object === null || typeof object !== 'object') return
|
|
45
|
-
if (object.type === 'string' && object.default?.[0] === '.')
|
|
46
|
-
return resolve(object.default.substring(1))
|
|
47
|
-
|
|
48
|
-
if (object.type === 'array') {
|
|
49
|
-
object.items = schema(object.items, resolve)
|
|
50
|
-
} else if (object.properties !== undefined) {
|
|
51
|
-
for (const [name, value] of Object.entries(object.properties)) {
|
|
52
|
-
if (value?.type === 'string' && value.default === '.')
|
|
53
|
-
object.properties[name] = resolve(name)
|
|
54
|
-
else object.properties[name] = schema(value, resolve)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return object
|
|
59
|
-
}
|
|
60
|
-
|
|
61
15
|
const forward = (operation, operations) => {
|
|
62
16
|
const target = operations[operation.forward]
|
|
63
17
|
|
package/src/.component/merge.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { merge } from '@toa.io/generic'
|
|
1
|
+
import { merge as combine } from '@toa.io/generic'
|
|
2
2
|
import { definition } from '../definition.js'
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
export const merge = async (root, manifest) => {
|
|
5
5
|
await Promise.all([
|
|
6
6
|
define(root, manifest, 'operations'),
|
|
7
7
|
define(root, manifest, 'events'),
|
|
@@ -22,7 +22,7 @@ const define = async (root, manifest, property) => {
|
|
|
22
22
|
const { define } = await bridged(bridge)
|
|
23
23
|
const definition = await define[singular](root, endpoint)
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
combine(item, definition)
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -45,7 +45,7 @@ const define = async (root, manifest, property) => {
|
|
|
45
45
|
|
|
46
46
|
try {
|
|
47
47
|
if (declared === undefined) manifest[property][endpoint] = item
|
|
48
|
-
else
|
|
48
|
+
else combine(declared, item)
|
|
49
49
|
} catch (error) {
|
|
50
50
|
console.error(`Error merging ${singular} '${endpoint}' in ${root}`)
|
|
51
51
|
throw error
|
|
@@ -69,5 +69,3 @@ async function bridged(reference) {
|
|
|
69
69
|
|
|
70
70
|
return module
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
export { bridge as merge }
|
package/src/context.js
CHANGED
|
@@ -16,11 +16,20 @@ import {
|
|
|
16
16
|
validate
|
|
17
17
|
} from './.context/index.js'
|
|
18
18
|
|
|
19
|
-
export const context = async (
|
|
19
|
+
export const context = async (
|
|
20
|
+
root,
|
|
21
|
+
environment = variables.get('TOA_ENV'),
|
|
22
|
+
options = {}
|
|
23
|
+
) => {
|
|
20
24
|
const path = resolve(root, CONTEXT)
|
|
21
25
|
const context = /** @type {toa.norm.Context} */ await read(path)
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
const names = environment?.split(':')
|
|
28
|
+
|
|
29
|
+
if (names !== undefined && names.length > 1 && names.some((name) => name.length === 0))
|
|
30
|
+
throw new Error(`Environment '${environment}' contains an empty name.`)
|
|
31
|
+
|
|
32
|
+
context.environment = names?.[0]
|
|
24
33
|
|
|
25
34
|
convolve(context, environment)
|
|
26
35
|
expand(context)
|
|
@@ -32,8 +41,9 @@ export const context = async (root, environment = variables.get('TOA_ENV')) => {
|
|
|
32
41
|
|
|
33
42
|
context.components = await Promise.all(paths.map(component))
|
|
34
43
|
|
|
35
|
-
// before the dependencies, which mark what only evicted components require
|
|
36
|
-
|
|
44
|
+
// before the dependencies, which mark what only evicted components require.
|
|
45
|
+
// `evicted: false` leaves that off: a local run of a component still needs its variables
|
|
46
|
+
if (options.evicted !== false) evict(context)
|
|
37
47
|
|
|
38
48
|
// what a context declares of every component that stores anything, its own and the ones its
|
|
39
49
|
// extensions bring, is given to them where those are known: inside `dependencies`
|
|
@@ -3,10 +3,6 @@ export const source = {
|
|
|
3
3
|
properties: {
|
|
4
4
|
foo: {
|
|
5
5
|
type: 'string'
|
|
6
|
-
},
|
|
7
|
-
bar: {
|
|
8
|
-
type: 'string',
|
|
9
|
-
default: '.foo'
|
|
10
6
|
}
|
|
11
7
|
}
|
|
12
8
|
},
|
|
@@ -15,42 +11,14 @@ export const source = {
|
|
|
15
11
|
input: {
|
|
16
12
|
properties: {
|
|
17
13
|
foo: {
|
|
18
|
-
type: 'string'
|
|
19
|
-
default: '.'
|
|
20
|
-
},
|
|
21
|
-
bar: {
|
|
22
|
-
type: 'string',
|
|
23
|
-
default: '.foo'
|
|
24
|
-
},
|
|
25
|
-
baz1: {
|
|
26
|
-
type: 'array',
|
|
27
|
-
items: {
|
|
28
|
-
type: 'string',
|
|
29
|
-
default: '.foo'
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
baz2: {
|
|
33
|
-
type: 'array',
|
|
34
|
-
items: {
|
|
35
|
-
properties: {
|
|
36
|
-
foo: {
|
|
37
|
-
type: 'string',
|
|
38
|
-
default: '.'
|
|
39
|
-
},
|
|
40
|
-
bar: {
|
|
41
|
-
type: 'string',
|
|
42
|
-
default: '.foo'
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
14
|
+
type: 'string'
|
|
46
15
|
}
|
|
47
16
|
}
|
|
48
17
|
},
|
|
49
18
|
output: {
|
|
50
19
|
properties: {
|
|
51
20
|
bar: {
|
|
52
|
-
type: 'string'
|
|
53
|
-
default: '.foo'
|
|
21
|
+
type: 'string'
|
|
54
22
|
}
|
|
55
23
|
}
|
|
56
24
|
}
|
|
@@ -67,9 +35,6 @@ export const target = {
|
|
|
67
35
|
properties: {
|
|
68
36
|
foo: {
|
|
69
37
|
type: 'string'
|
|
70
|
-
},
|
|
71
|
-
bar: {
|
|
72
|
-
type: 'string'
|
|
73
38
|
}
|
|
74
39
|
}
|
|
75
40
|
},
|
|
@@ -79,28 +44,6 @@ export const target = {
|
|
|
79
44
|
properties: {
|
|
80
45
|
foo: {
|
|
81
46
|
type: 'string'
|
|
82
|
-
},
|
|
83
|
-
bar: {
|
|
84
|
-
type: 'string'
|
|
85
|
-
},
|
|
86
|
-
baz1: {
|
|
87
|
-
type: 'array',
|
|
88
|
-
items: {
|
|
89
|
-
type: 'string'
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
baz2: {
|
|
93
|
-
type: 'array',
|
|
94
|
-
items: {
|
|
95
|
-
properties: {
|
|
96
|
-
foo: {
|
|
97
|
-
type: 'string'
|
|
98
|
-
},
|
|
99
|
-
bar: {
|
|
100
|
-
type: 'string'
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
47
|
}
|
|
105
48
|
}
|
|
106
49
|
},
|
|
@@ -119,28 +62,6 @@ export const target = {
|
|
|
119
62
|
properties: {
|
|
120
63
|
foo: {
|
|
121
64
|
type: 'string'
|
|
122
|
-
},
|
|
123
|
-
bar: {
|
|
124
|
-
type: 'string'
|
|
125
|
-
},
|
|
126
|
-
baz1: {
|
|
127
|
-
type: 'array',
|
|
128
|
-
items: {
|
|
129
|
-
type: 'string'
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
baz2: {
|
|
133
|
-
type: 'array',
|
|
134
|
-
items: {
|
|
135
|
-
properties: {
|
|
136
|
-
foo: {
|
|
137
|
-
type: 'string'
|
|
138
|
-
},
|
|
139
|
-
bar: {
|
|
140
|
-
type: 'string'
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
65
|
}
|
|
145
66
|
}
|
|
146
67
|
},
|
|
@@ -18,20 +18,6 @@ it('should dereference', () => {
|
|
|
18
18
|
assert.deepStrictEqual(source, fixtures.target)
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
it('should throw on invalid schema reference', () => {
|
|
22
|
-
source.operations.transit.output.properties.baz = { type: 'string', default: '.' }
|
|
23
|
-
assert.throws(
|
|
24
|
-
() => dereference(source),
|
|
25
|
-
(error) => /is not defined/.test(error.message)
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
source.operations.transit.output.properties.baz = { type: 'string', default: '.baz' }
|
|
29
|
-
assert.throws(
|
|
30
|
-
() => dereference(source),
|
|
31
|
-
(error) => /is not defined/.test(error.message)
|
|
32
|
-
)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
21
|
it('should throw on invalid forwarding', () => {
|
|
36
22
|
source.operations.create.forward = 'foo'
|
|
37
23
|
assert.throws(
|
|
@@ -25,6 +25,14 @@ describe('operations', () => {
|
|
|
25
25
|
assert.deepStrictEqual(manifest.operations.add.output, {})
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
+
it('should state no input for an operation that takes none', async () => {
|
|
29
|
+
manifest.operations.compute = { type: 'computation', input: null }
|
|
30
|
+
|
|
31
|
+
await normalize(manifest)
|
|
32
|
+
|
|
33
|
+
assert.equal('input' in manifest.operations.compute, false)
|
|
34
|
+
})
|
|
35
|
+
|
|
28
36
|
it('should scope a computation to none', async () => {
|
|
29
37
|
manifest.operations.compute = { type: 'computation' }
|
|
30
38
|
|
package/types/context.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Manifest } from './component.
|
|
1
|
+
import { Manifest } from './component.d.ts'
|
|
2
2
|
import { Locator } from '@toa.io/core'
|
|
3
|
-
import type { Declaration } from './context/declaration.
|
|
3
|
+
import type { Declaration } from './context/declaration.d.ts'
|
|
4
4
|
|
|
5
5
|
interface Runtime {
|
|
6
6
|
version: string
|
|
@@ -44,4 +44,11 @@ interface Context extends Declaration {
|
|
|
44
44
|
packages?: Record<string, string>
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
export function context(
|
|
47
|
+
export function context(
|
|
48
|
+
path: string,
|
|
49
|
+
environment?: string,
|
|
50
|
+
options?: {
|
|
51
|
+
/** `false` does not apply eviction: every component is treated as deployed */
|
|
52
|
+
evicted?: boolean
|
|
53
|
+
}
|
|
54
|
+
): Promise<Context>
|
package/types/entity.d.ts
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export * as context from './context.
|
|
2
|
-
export * as component from './component.
|
|
1
|
+
export * as context from './context.d.ts'
|
|
2
|
+
export * as component from './component.d.ts'
|
|
3
3
|
|
|
4
|
-
export { Manifest } from './component.
|
|
5
|
-
export * as entity from './entity.
|
|
4
|
+
export { Manifest } from './component.d.ts'
|
|
5
|
+
export * as entity from './entity.d.ts'
|
|
6
6
|
|
|
7
7
|
export interface Definition {
|
|
8
8
|
/** The package's name, or the reference where it has no package.json */
|