@toa.io/norm 1.0.0-alpha.3 → 1.0.0-alpha.300
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 +124 -0
- package/package.json +9 -7
- package/src/.component/.expand/bridge.js +2 -6
- package/src/.component/.expand/entity.js +2 -9
- package/src/.component/.expand/events.js +2 -6
- package/src/.component/.expand/extensions.js +5 -8
- package/src/.component/.expand/index.js +7 -17
- package/src/.component/.expand/operations.js +2 -9
- package/src/.component/.expand/receivers.js +4 -7
- package/src/.component/.expand/version.js +55 -0
- package/src/.component/.normalize/entity.js +7 -0
- package/src/.component/.normalize/events.js +16 -4
- package/src/.component/.normalize/index.js +4 -9
- package/src/.component/.normalize/operations.js +10 -6
- package/src/.component/.normalize/receivers.js +4 -5
- package/src/.component/collapse.js +60 -9
- package/src/.component/defaults.js +16 -25
- package/src/.component/dependencies.js +14 -10
- package/src/.component/dereference.js +20 -18
- package/src/.component/expand.js +6 -9
- package/src/.component/extensions.js +9 -10
- package/src/.component/index.js +10 -21
- package/src/.component/merge.js +25 -13
- package/src/.component/migrations.js +57 -0
- package/src/.component/normalize.js +10 -6
- package/src/.component/schema.yaml +139 -59
- package/src/.component/validate.js +37 -17
- package/src/.context/.dependencies/connectors.js +11 -10
- package/src/.context/.dependencies/describe.js +4 -5
- package/src/.context/.dependencies/extensions.js +93 -20
- package/src/.context/.dependencies/index.js +3 -9
- package/src/.context/.dependencies/resolve.js +44 -7
- package/src/.context/complete.js +1 -5
- package/src/.context/converge.js +31 -0
- package/src/.context/dependencies.js +43 -6
- package/src/.context/dereference.js +11 -6
- package/src/.context/evict.js +38 -0
- package/src/.context/expand.js +11 -6
- package/src/.context/index.js +8 -15
- package/src/.context/normalize.js +9 -7
- package/src/.context/schema.yaml +179 -11
- package/src/.context/validate.js +8 -14
- package/src/component.js +37 -18
- package/src/context.js +33 -14
- package/src/definition.js +56 -0
- package/src/entity.js +15 -0
- package/src/index.js +6 -9
- package/src/manifest.js +154 -0
- package/src/shortcuts.js +9 -14
- package/test/component/collapse.fixtures.js +30 -39
- package/test/component/collapse.test.js +90 -10
- package/test/component/dependencies.test.js +16 -14
- package/test/component/dereference.fixtures.js +15 -24
- package/test/component/dereference.test.js +18 -8
- package/test/component/dummies/extension/index.js +1 -5
- package/test/component/dummies/migrations/conflicting/migrations/0001-one.yaml +2 -0
- package/test/component/dummies/migrations/conflicting/migrations/0001-one.yml +2 -0
- package/test/component/dummies/migrations/malformed/migrations/0001-one.yaml +1 -0
- package/test/component/dummies/migrations/ordered/migrations/0001-first.yml +4 -0
- package/test/component/dummies/migrations/ordered/migrations/0002-second.yaml +4 -0
- package/test/component/dummies/migrations/ordered/migrations/0010-third.json +1 -0
- package/test/component/dummies/migrations/stateless/migrations/0001-one.yaml +2 -0
- package/test/component/expand.fixtures.js +13 -12
- package/test/component/expand.test.js +17 -13
- package/test/component/migrations.test.js +62 -0
- package/test/component/normalize.fixtures.js +3 -7
- package/test/component/normalize.test.js +86 -28
- package/test/component/validate.fixtures.js +15 -13
- package/test/component/validate.test.js +181 -107
- package/test/context/complete.fixtures.js +2 -8
- package/test/context/complete.test.js +14 -8
- package/test/context/converge.test.js +57 -0
- package/test/context/dereference.fixtures.js +3 -9
- package/test/context/dereference.test.js +18 -7
- package/test/context/evict.fixtures.js +39 -0
- package/test/context/evict.test.js +72 -0
- package/test/context/expand.fixtures.js +1 -5
- package/test/context/expand.test.js +36 -8
- package/test/context/normalize.fixtures.js +2 -7
- package/test/context/normalize.test.js +33 -9
- package/test/context/validate.fixtures.js +1 -6
- package/test/context/validate.test.js +143 -34
- package/test/definition.test.js +35 -0
- package/test/dummies/bare/index.js +1 -0
- package/test/dummies/defined/definition.js +1 -0
- package/test/dummies/defined/index.js +1 -0
- package/test/dummies/defined/package.json +1 -0
- package/test/manifest.test.js +126 -0
- package/test/shortcuts.fixtures.js +4 -9
- package/test/shortcuts.test.js +17 -15
- package/types/component.d.ts +24 -5
- package/types/context/declaration.d.ts +33 -3
- package/types/context.d.ts +19 -13
- package/types/entity.d.ts +7 -0
- package/types/index.d.ts +12 -3
- package/src/.component/.expand/properties.js +0 -14
- package/src/.context/.dependencies/load.js +0 -36
- package/test/context/dependencies.load.test.js +0 -24
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
# [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
|
+
|
|
8
|
+
* feat(boot)!: a process is a thing, and an extension may keep something in one ([528d6cd](https://github.com/toa-io/toa/commit/528d6cdd307b45a6c30b6a184d7e06b3328cecf8))
|
|
9
|
+
* feat(core)!: an operation may ask to run once ([dca616c](https://github.com/toa-io/toa/commit/dca616cb380a7a389e277875d3bd5ca55a6e4d45))
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **core:** a call may name the process it goes to, and wait for a set time ([7d8adb2](https://github.com/toa-io/toa/commit/7d8adb2772a95ff45ae5374efdedfb5a857fbbe9))
|
|
14
|
+
* **core:** an assignment may ask to run once as well ([10fbcbd](https://github.com/toa-io/toa/commit/10fbcbd86a5e82960e65f62c09d0a465ae8d0353))
|
|
15
|
+
* **norm:** a context says what it does not deploy ([135e6fd](https://github.com/toa-io/toa/commit/135e6fdfbc4270762028e9e5a0516766960ce59c))
|
|
16
|
+
|
|
17
|
+
### BREAKING CHANGES
|
|
18
|
+
|
|
19
|
+
* `Storage.store`, `.upsert` and `.ensure` take one more
|
|
20
|
+
argument, and `Storage` has two more members. A connector that ignores them
|
|
21
|
+
works exactly as it did; a component that asks it for `once` is refused at
|
|
22
|
+
boot. See migrations/299.md.
|
|
23
|
+
* a composition booted directly has no readiness probe. Readiness is
|
|
24
|
+
the process's, and a composition is not a process. `stage.workload` boots one where a
|
|
25
|
+
suite needs it.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# [1.0.0-alpha.299](https://github.com/toa-io/toa/compare/v1.0.0-alpha.298...v1.0.0-alpha.299) (2026-09-10)
|
|
29
|
+
|
|
30
|
+
### Features
|
|
31
|
+
|
|
32
|
+
* **convergence:** deployments of one context converge ([8a70343](https://github.com/toa-io/toa/commit/8a70343cf6953aab58d4a25bac215508ff8d9a37))
|
|
33
|
+
* **convergence:** what an extension ships converges too ([b3be33d](https://github.com/toa-io/toa/commit/b3be33d2a1db45cfd844288467441aea2f4a722d))
|
|
34
|
+
* **core:** a record carries the region that wrote it ([13ed187](https://github.com/toa-io/toa/commit/13ed187604f06dec66feec2e26ccf9dae30bb048))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# [1.0.0-alpha.298](https://github.com/toa-io/toa/compare/v1.0.0-alpha.297...v1.0.0-alpha.298) (2026-09-08)
|
|
38
|
+
|
|
39
|
+
**Note:** Version bump only for package @toa.io/norm
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# [1.0.0-alpha.297](https://github.com/toa-io/toa/compare/v1.0.0-alpha.296...v1.0.0-alpha.297) (2026-09-08)
|
|
46
|
+
|
|
47
|
+
**Note:** Version bump only for package @toa.io/norm
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# [1.0.0-alpha.296](https://github.com/toa-io/toa/compare/v1.0.0-alpha.295...v1.0.0-alpha.296) (2026-09-08)
|
|
54
|
+
|
|
55
|
+
**Note:** Version bump only for package @toa.io/norm
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# [1.0.0-alpha.295](https://github.com/toa-io/toa/compare/v1.0.0-alpha.294...v1.0.0-alpha.295) (2026-09-07)
|
|
62
|
+
|
|
63
|
+
**Note:** Version bump only for package @toa.io/norm
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# [1.0.0-alpha.294](https://github.com/toa-io/toa/compare/v1.0.0-alpha.293...v1.0.0-alpha.294) (2026-09-07)
|
|
70
|
+
|
|
71
|
+
**Note:** Version bump only for package @toa.io/norm
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# [1.0.0-alpha.293](https://github.com/toa-io/toa/compare/v1.0.0-alpha.292...v1.0.0-alpha.293) (2026-09-07)
|
|
78
|
+
|
|
79
|
+
**Note:** Version bump only for package @toa.io/norm
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# [1.0.0-alpha.292](https://github.com/toa-io/toa/compare/v1.0.0-alpha.291...v1.0.0-alpha.292) (2026-09-07)
|
|
86
|
+
|
|
87
|
+
### Features
|
|
88
|
+
|
|
89
|
+
* **cli:** the deployment library is a peer, and the binary is the CLI's ([a923f81](https://github.com/toa-io/toa/commit/a923f8173a55ed1c091213a7c7db1863ca1d0ba6))
|
|
90
|
+
* **definitions:** the version of Toa is the definitions package's ([3d0272e](https://github.com/toa-io/toa/commit/3d0272ea0ab862f4b6c2e58a2f003f92d0f8a87f))
|
|
91
|
+
* **definitions:** what a package declares is read from one package ([7052341](https://github.com/toa-io/toa/commit/70523411b5d9e2b204c999aa02365173f3bb518d))
|
|
92
|
+
* **norm:** a package's definition is read by one resolver ([bffd41c](https://github.com/toa-io/toa/commit/bffd41c8d18c42786b0bf321623088b9dabc4724))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# [1.0.0-alpha.291](https://github.com/toa-io/toa/compare/v1.0.0-alpha.290...v1.0.0-alpha.291) (2026-09-07)
|
|
96
|
+
|
|
97
|
+
**Note:** Version bump only for package @toa.io/norm
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# [1.0.0-alpha.289](https://github.com/toa-io/toa/compare/v1.0.0-alpha.288...v1.0.0-alpha.289) (2026-09-07)
|
|
104
|
+
|
|
105
|
+
* A deploy moves only what changed, and a component sees none of the runtime's environment (#1073) ([f38e3db](https://github.com/toa-io/toa/commit/f38e3db533f24db866c57bfa1ef294eff1eaf499)), closes [#1073](https://github.com/toa-io/toa/issues/1073) [#1064](https://github.com/toa-io/toa/issues/1064) [#1066](https://github.com/toa-io/toa/issues/1066) [#1067](https://github.com/toa-io/toa/issues/1067) [#1068](https://github.com/toa-io/toa/issues/1068) [#1069](https://github.com/toa-io/toa/issues/1069) [#1071](https://github.com/toa-io/toa/issues/1071) [#1070](https://github.com/toa-io/toa/issues/1070) [#1072](https://github.com/toa-io/toa/issues/1072)
|
|
106
|
+
|
|
107
|
+
### BREAKING CHANGES
|
|
108
|
+
|
|
109
|
+
* a component that read `process.env.TOA_*` reads `context` instead;
|
|
110
|
+
`echo(input)` no longer substitutes from the environment; a bash operation sees no
|
|
111
|
+
`TOA_*`; images no longer set `USER node` — see migrations/289.md.
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# [1.0.0-alpha.288](https://github.com/toa-io/toa/compare/v1.0.0-alpha.287...v1.0.0-alpha.288) (2026-09-06)
|
|
115
|
+
|
|
116
|
+
**Note:** Version bump only for package @toa.io/norm
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# [1.0.0-alpha.287](https://github.com/toa-io/toa/compare/v1.0.0-alpha.286...v1.0.0-alpha.287) (2026-09-06)
|
|
123
|
+
|
|
124
|
+
**Note:** Version bump only for package @toa.io/norm
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/norm",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.300",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "Toa declarations normalization and validation",
|
|
5
6
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
6
7
|
"homepage": "https://github.com/toa-io/toa#readme",
|
|
@@ -20,11 +21,12 @@
|
|
|
20
21
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@toa.io/core": "1.0.0-alpha.
|
|
24
|
-
"@toa.io/
|
|
25
|
-
"@toa.io/
|
|
26
|
-
"@toa.io/
|
|
27
|
-
"@toa.io/
|
|
24
|
+
"@toa.io/core": "1.0.0-alpha.300",
|
|
25
|
+
"@toa.io/definitions": "1.0.0-alpha.300",
|
|
26
|
+
"@toa.io/generic": "1.0.0-alpha.292",
|
|
27
|
+
"@toa.io/prototype": "1.0.0-alpha.300",
|
|
28
|
+
"@toa.io/schemas": "1.0.0-alpha.299",
|
|
29
|
+
"fast-glob": "3.3.3"
|
|
28
30
|
},
|
|
29
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "f91131a9aeb29aa271f177c4055ddcf0cda8c75e"
|
|
30
32
|
}
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { resolve } from '../../shortcuts.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const { resolve } = require('../../shortcuts')
|
|
6
|
-
|
|
7
|
-
function entity (manifest) {
|
|
3
|
+
export function entity(manifest) {
|
|
8
4
|
if (!('entity' in manifest)) return
|
|
9
|
-
if ('schema' in manifest.entity) manifest.entity.schema = expand(manifest.entity.schema)
|
|
10
5
|
|
|
11
6
|
manifest.entity.storage = resolve(manifest.entity.storage)
|
|
12
7
|
}
|
|
13
|
-
|
|
14
|
-
exports.entity = entity
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { resolve } from '../../shortcuts.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function events (manifest) {
|
|
3
|
+
export function events(manifest) {
|
|
6
4
|
if (manifest.events === undefined) return
|
|
7
5
|
|
|
8
6
|
for (const event of Object.values(manifest.events)) {
|
|
@@ -10,5 +8,3 @@ function events (manifest) {
|
|
|
10
8
|
if (event.bridge !== undefined) event.bridge = resolve(event.bridge)
|
|
11
9
|
}
|
|
12
10
|
}
|
|
13
|
-
|
|
14
|
-
exports.events = events
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { recognize } from '../../shortcuts.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function extensions (manifest) {
|
|
3
|
+
export function extensions(manifest) {
|
|
6
4
|
recognize(SHORTCUTS, manifest, 'extensions')
|
|
7
5
|
recognize(SHORTCUTS, manifest.extensions)
|
|
8
6
|
}
|
|
@@ -10,11 +8,10 @@ function extensions (manifest) {
|
|
|
10
8
|
const SHORTCUTS = {
|
|
11
9
|
exposition: '@toa.io/extensions.exposition',
|
|
12
10
|
realtime: '@toa.io/extensions.realtime',
|
|
13
|
-
origins: '@toa.io/extensions.origins',
|
|
14
11
|
configuration: '@toa.io/extensions.configuration',
|
|
15
12
|
state: '@toa.io/extensions.state',
|
|
16
13
|
stash: '@toa.io/extensions.stash',
|
|
17
|
-
storages: '@toa.io/extensions.storages'
|
|
14
|
+
storages: '@toa.io/extensions.storages',
|
|
15
|
+
cadence: '@toa.io/extensions.cadence',
|
|
16
|
+
introspection: '@toa.io/extensions.introspection'
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
exports.extensions = extensions
|
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const { properties } = require('./properties')
|
|
9
|
-
const { receivers } = require('./receivers')
|
|
10
|
-
|
|
11
|
-
exports.bridge = bridge
|
|
12
|
-
exports.entity = entity
|
|
13
|
-
exports.events = events
|
|
14
|
-
exports.extensions = extensions
|
|
15
|
-
exports.operations = operations
|
|
16
|
-
exports.properties = properties
|
|
17
|
-
exports.receivers = receivers
|
|
1
|
+
export { bridge } from './bridge.js'
|
|
2
|
+
export { entity } from './entity.js'
|
|
3
|
+
export { events } from './events.js'
|
|
4
|
+
export { extensions } from './extensions.js'
|
|
5
|
+
export { operations } from './operations.js'
|
|
6
|
+
export { receivers } from './receivers.js'
|
|
7
|
+
export { version } from './version.js'
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { resolve } from '../../shortcuts.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const { resolve } = require('../../shortcuts')
|
|
5
|
-
|
|
6
|
-
function operations (manifest) {
|
|
3
|
+
export function operations(manifest) {
|
|
7
4
|
if (manifest.operations === undefined) return
|
|
8
5
|
|
|
9
6
|
for (const operation of Object.values(manifest.operations)) {
|
|
10
|
-
if (operation.input !== undefined) operation.input = expand(operation.input)
|
|
11
|
-
if (operation.output !== undefined) operation.output = expand(operation.output)
|
|
12
7
|
if (operation.bridge !== undefined) operation.bridge = resolve(operation.bridge)
|
|
13
8
|
|
|
14
9
|
if (operation.bindings !== undefined && operation.bindings !== null) {
|
|
@@ -16,5 +11,3 @@ function operations (manifest) {
|
|
|
16
11
|
}
|
|
17
12
|
}
|
|
18
13
|
}
|
|
19
|
-
|
|
20
|
-
exports.operations = operations
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { resolve } from '../../shortcuts.js'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function receivers (manifest) {
|
|
3
|
+
export function receivers(manifest) {
|
|
6
4
|
if (manifest.receivers === undefined) return
|
|
7
5
|
|
|
8
6
|
for (const [locator, receiver] of Object.entries(manifest.receivers)) {
|
|
9
|
-
if (typeof receiver === 'string')
|
|
7
|
+
if (typeof receiver === 'string')
|
|
8
|
+
manifest.receivers[locator] = { operation: receiver }
|
|
10
9
|
|
|
11
10
|
if (receiver.binding !== undefined) receiver.binding = resolve(receiver.binding)
|
|
12
11
|
if (receiver.bridge !== undefined) receiver.bridge = resolve(receiver.bridge)
|
|
13
12
|
}
|
|
14
13
|
}
|
|
15
|
-
|
|
16
|
-
exports.receivers = receivers
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
4
|
+
|
|
5
|
+
export async function version(manifest) {
|
|
6
|
+
manifest.version ??= await hash(manifest.path)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Identifies a build of a component: it becomes the tag of its image, so it must be the
|
|
11
|
+
* same for the same sources and different for different ones — on any machine.
|
|
12
|
+
*
|
|
13
|
+
* What the image excludes cannot change it: `node_modules` is in the `.dockerignore` the
|
|
14
|
+
* build writes (see `operations/src/deployment/images/image.js`), so hashing it would only
|
|
15
|
+
* retag an identical image after a local install.
|
|
16
|
+
*/
|
|
17
|
+
async function hash(path) {
|
|
18
|
+
const files = (await list(path)).sort()
|
|
19
|
+
const digests = await Promise.all(files.map((file) => digest(join(path, file))))
|
|
20
|
+
const total = createHash('sha256')
|
|
21
|
+
|
|
22
|
+
// the path is part of it: moving a file changes the build even if no content did
|
|
23
|
+
for (let i = 0; i < files.length; i++) total.update(files[i]).update(digests[i])
|
|
24
|
+
|
|
25
|
+
return total.digest('hex').slice(0, 8)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string} root
|
|
30
|
+
* @param {string} [path] relative
|
|
31
|
+
* @param {string[]} [acc]
|
|
32
|
+
* @returns {Promise<string[]>} paths relative to `root`
|
|
33
|
+
*/
|
|
34
|
+
async function list(root, path = '', acc = []) {
|
|
35
|
+
const entries = await fs.readdir(join(root, path), { withFileTypes: true })
|
|
36
|
+
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
if (EXCLUDED.has(entry.name)) continue
|
|
39
|
+
|
|
40
|
+
const relative = path === '' ? entry.name : `${path}/${entry.name}`
|
|
41
|
+
|
|
42
|
+
if (entry.isDirectory()) await list(root, relative, acc)
|
|
43
|
+
else if (entry.isFile()) acc.push(relative)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return acc
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function digest(path) {
|
|
50
|
+
return createHash('sha256')
|
|
51
|
+
.update(await fs.readFile(path))
|
|
52
|
+
.digest()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const EXCLUDED = new Set(['node_modules', '.git'])
|
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
import { definition } from '../../definition.js'
|
|
2
2
|
|
|
3
|
-
const events = (component) => {
|
|
3
|
+
export const events = async (component) => {
|
|
4
4
|
if (component.events === undefined) return
|
|
5
5
|
|
|
6
|
-
const binding = component.bindings
|
|
6
|
+
const binding = await asynchronous(component.bindings)
|
|
7
7
|
|
|
8
8
|
for (const event of Object.values(component.events)) {
|
|
9
9
|
if (event.binding === undefined) event.binding = binding
|
|
10
|
+
|
|
11
|
+
event.conditioned ??= false
|
|
12
|
+
event.subjective ??= false
|
|
10
13
|
}
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
/** The first binding that carries events, which only its module can say. */
|
|
17
|
+
async function asynchronous(bindings) {
|
|
18
|
+
for (const binding of bindings) {
|
|
19
|
+
const { properties } = (await definition(binding)).module
|
|
20
|
+
|
|
21
|
+
if (properties.async === true) return binding
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return undefined
|
|
25
|
+
}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const { receivers } = require('./receivers')
|
|
6
|
-
|
|
7
|
-
exports.events = events
|
|
8
|
-
exports.operations = operations
|
|
9
|
-
exports.receivers = receivers
|
|
1
|
+
export { entity } from './entity.js'
|
|
2
|
+
export { events } from './events.js'
|
|
3
|
+
export { operations } from './operations.js'
|
|
4
|
+
export { receivers } from './receivers.js'
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const operations = (component) => {
|
|
1
|
+
export const operations = (component) => {
|
|
4
2
|
if (component.operations === undefined) return
|
|
5
3
|
|
|
6
4
|
for (const [endpoint, operation] of Object.entries(component.operations)) {
|
|
7
|
-
|
|
5
|
+
// what an operation of this type acquires, where it did not say
|
|
6
|
+
if (operation.type === 'computation' || operation.type === 'unmanaged') {
|
|
7
|
+
operation.scope = 'none'
|
|
8
8
|
operation.query = false
|
|
9
|
+
} else if (operation.type === 'effect') operation.scope ??= 'none'
|
|
10
|
+
|
|
11
|
+
if (operation.scope === 'none') operation.query = false
|
|
12
|
+
|
|
13
|
+
// an operation that states no output states an empty schema, which every reply fits
|
|
14
|
+
operation.output ??= {}
|
|
9
15
|
|
|
10
16
|
if (operation.bindings === undefined) operation.bindings = component.bindings
|
|
11
17
|
if (operation.bindings === null) operation.bindings = []
|
|
12
18
|
if (operation.virtual === true) delete component.operations[endpoint]
|
|
13
19
|
}
|
|
14
20
|
}
|
|
15
|
-
|
|
16
|
-
exports.operations = operations
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
function receivers (component) {
|
|
1
|
+
export function receivers(component) {
|
|
4
2
|
if (component.receivers === undefined) return
|
|
5
3
|
|
|
6
4
|
const receivers = component.receivers
|
|
@@ -9,6 +7,9 @@ function receivers (component) {
|
|
|
9
7
|
const segments = key.split('.')
|
|
10
8
|
const source = value.source ?? 'default'
|
|
11
9
|
|
|
10
|
+
value.conditioned ??= false
|
|
11
|
+
value.adaptive ??= false
|
|
12
|
+
|
|
12
13
|
if (source !== 'default') continue
|
|
13
14
|
if (segments.length === 3) continue // already with a namespace
|
|
14
15
|
|
|
@@ -18,5 +19,3 @@ function receivers (component) {
|
|
|
18
19
|
receivers[newKey] = value
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
exports.receivers = receivers
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { merge } from '@toa.io/generic'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const collapse = (manifest, prototype) => {
|
|
3
|
+
export const collapse = (manifest, prototype) => {
|
|
6
4
|
delete manifest.prototype
|
|
7
5
|
|
|
8
6
|
if (prototype.operations) {
|
|
@@ -14,7 +12,8 @@ const collapse = (manifest, prototype) => {
|
|
|
14
12
|
if (manifest.operations === undefined) manifest.operations = {}
|
|
15
13
|
|
|
16
14
|
for (let [endpoint, operation] of operations) {
|
|
17
|
-
if (manifest.operations[endpoint] === undefined)
|
|
15
|
+
if (manifest.operations[endpoint] === undefined)
|
|
16
|
+
manifest.operations[endpoint] = {}
|
|
18
17
|
else {
|
|
19
18
|
const { virtual, ...real } = operation
|
|
20
19
|
|
|
@@ -26,7 +25,8 @@ const collapse = (manifest, prototype) => {
|
|
|
26
25
|
merge(manifest.operations[endpoint], declaration)
|
|
27
26
|
|
|
28
27
|
if (bridge !== undefined) {
|
|
29
|
-
if (manifest.prototype.operations === undefined)
|
|
28
|
+
if (manifest.prototype.operations === undefined)
|
|
29
|
+
manifest.prototype.operations = {}
|
|
30
30
|
|
|
31
31
|
manifest.prototype.operations[endpoint] = { bridge }
|
|
32
32
|
}
|
|
@@ -36,13 +36,64 @@ const collapse = (manifest, prototype) => {
|
|
|
36
36
|
|
|
37
37
|
const { entity, events, extensions } = prototype
|
|
38
38
|
|
|
39
|
-
if (
|
|
39
|
+
if (
|
|
40
|
+
manifest.entity?.properties?.id !== undefined &&
|
|
41
|
+
entity?.properties?.id !== undefined
|
|
42
|
+
) {
|
|
40
43
|
manifest.entity.custom = true
|
|
41
44
|
|
|
42
|
-
delete prototype.entity.
|
|
45
|
+
delete prototype.entity.properties.id
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
/*
|
|
49
|
+
* `id` is the only one a component may state for itself. The rest are written by the runtime
|
|
50
|
+
* and read by it, so one redeclared here would be merged over what the prototype says and
|
|
51
|
+
* change what the record holds, or what a criterion against it compares — without the
|
|
52
|
+
* component that wrote it having anything to do with either.
|
|
53
|
+
*/
|
|
54
|
+
for (const name of SYSTEM)
|
|
55
|
+
if (
|
|
56
|
+
manifest.entity?.properties?.[name] !== undefined &&
|
|
57
|
+
entity?.properties?.[name] !== undefined
|
|
58
|
+
)
|
|
59
|
+
throw new Error(`System property '${name}' cannot be overridden`)
|
|
60
|
+
|
|
61
|
+
if (prototype.events !== undefined && manifest.events !== undefined)
|
|
62
|
+
for (const event of Object.keys(prototype.events))
|
|
63
|
+
if (event in manifest.events) delete prototype.events[event]
|
|
64
|
+
|
|
65
|
+
if (entity?.migrations !== undefined) inherit(manifest, prototype)
|
|
66
|
+
|
|
45
67
|
merge(manifest, { entity, events, extensions })
|
|
46
68
|
}
|
|
47
69
|
|
|
48
|
-
|
|
70
|
+
/**
|
|
71
|
+
* What a prototype declares is in every record of its descendants, so its migrations are applied
|
|
72
|
+
* to each descendant's collection — ahead of the descendant's own, which are written against
|
|
73
|
+
* what they leave. An inherited one is recorded under the prototype's name, so that it collides
|
|
74
|
+
* with nothing the component names itself; one inherited from further up already carries its
|
|
75
|
+
* name and is passed on as it is.
|
|
76
|
+
*/
|
|
77
|
+
function inherit(manifest, prototype) {
|
|
78
|
+
const inherited = prototype.entity.migrations.map((migration) =>
|
|
79
|
+
migration.prototype === undefined
|
|
80
|
+
? {
|
|
81
|
+
...migration,
|
|
82
|
+
id: `${prototype.name}:${migration.id}`,
|
|
83
|
+
prototype: prototype.name
|
|
84
|
+
}
|
|
85
|
+
: migration
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
const own = manifest.entity?.migrations ?? []
|
|
89
|
+
|
|
90
|
+
if (manifest.entity === undefined) manifest.entity = {}
|
|
91
|
+
|
|
92
|
+
manifest.entity.migrations = [...inherited, ...own]
|
|
93
|
+
|
|
94
|
+
// merged below by `merge`, which would append them a second time
|
|
95
|
+
delete prototype.entity.migrations
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** What the runtime writes into every record. `id` is not among them: a component may own it. */
|
|
99
|
+
const SYSTEM = ['VERSION', 'CREATED', 'UPDATED', 'DELETED', 'REGION']
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const { hash } = require('@toa.io/generic')
|
|
4
|
-
const assert = require('node:assert')
|
|
1
|
+
import { hash } from '@toa.io/generic'
|
|
5
2
|
|
|
6
3
|
// these defaults are required before validation
|
|
7
|
-
const defaults = (manifest) => {
|
|
8
|
-
if (manifest.name === undefined)
|
|
4
|
+
export const defaults = (manifest, proto) => {
|
|
5
|
+
if (manifest.name === undefined)
|
|
6
|
+
if (proto) manifest.name = protoName(manifest)
|
|
7
|
+
else nameAfterDir(manifest)
|
|
8
|
+
|
|
9
9
|
if (manifest.bindings === undefined) manifest.bindings = ['@toa.io/bindings.amqp']
|
|
10
10
|
if (manifest.bridge === undefined) manifest.bridge = '@toa.io/bridges.node'
|
|
11
11
|
|
|
@@ -13,30 +13,21 @@ const defaults = (manifest) => {
|
|
|
13
13
|
if (manifest.entity.storage === null)
|
|
14
14
|
manifest.entity.storage = '@toa.io/storages.null'
|
|
15
15
|
} else {
|
|
16
|
-
if (manifest.prototype === undefined)
|
|
17
|
-
manifest.prototype = null
|
|
16
|
+
if (manifest.prototype === undefined) manifest.prototype = null
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
if (manifest.prototype === undefined) manifest.prototype = '@toa.io/prototype'
|
|
21
|
-
|
|
22
|
-
// TODO: bridge.version()
|
|
23
|
-
|
|
24
|
-
if (manifest.version === undefined) {
|
|
25
|
-
const bridge = require(manifest.bridge)
|
|
26
|
-
|
|
27
|
-
if ('version' in bridge)
|
|
28
|
-
manifest.version = bridge.version(manifest)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (manifest.version === undefined) {
|
|
32
|
-
console.warn(`Component '${manifest.namespace ? manifest.namespace + '.' : ''}${manifest.name}' has no version`)
|
|
33
|
-
|
|
34
|
-
manifest.version = Math.random().toString(36).slice(2)
|
|
35
|
-
}
|
|
36
20
|
}
|
|
37
21
|
|
|
38
|
-
function protoName
|
|
22
|
+
function protoName(manifest) {
|
|
39
23
|
return 'proto' + hash(manifest.path)
|
|
40
24
|
}
|
|
41
25
|
|
|
42
|
-
|
|
26
|
+
function nameAfterDir(manifest) {
|
|
27
|
+
const parts = manifest.path.split('/')
|
|
28
|
+
const dirname = parts[parts.length - 1]
|
|
29
|
+
const [name, namespace] = dirname.split('.').reverse()
|
|
30
|
+
|
|
31
|
+
manifest.name = name
|
|
32
|
+
manifest.namespace = namespace
|
|
33
|
+
}
|
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import { join, dirname } from 'node:path'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
// import.meta.resolve takes no paths, and a storage is resolved against the
|
|
5
|
+
// component that names it
|
|
6
|
+
const require = createRequire(import.meta.url)
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* @param {toa.norm.Component} component
|
|
7
10
|
*/
|
|
8
|
-
const dependencies = (component) => {
|
|
9
|
-
if ('entity' in component)
|
|
11
|
+
export const dependencies = (component) => {
|
|
12
|
+
if ('entity' in component)
|
|
13
|
+
component.entity.storage = resolve(component.path, component.entity.storage)
|
|
10
14
|
}
|
|
11
15
|
|
|
12
|
-
function resolve
|
|
13
|
-
const paths = [root,
|
|
16
|
+
function resolve(root, reference) {
|
|
17
|
+
const paths = [root, import.meta.dirname]
|
|
14
18
|
const options = { paths }
|
|
15
19
|
|
|
16
20
|
let path
|
|
17
21
|
|
|
18
|
-
try {
|
|
22
|
+
try {
|
|
23
|
+
// as package
|
|
19
24
|
const packageJsonRef = join(reference, 'package.json')
|
|
20
25
|
|
|
21
26
|
path = require.resolve(packageJsonRef, options)
|
|
22
|
-
} catch {
|
|
27
|
+
} catch {
|
|
28
|
+
// as directory
|
|
23
29
|
path = require.resolve(reference, options)
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
return dirname(path)
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
exports.dependencies = dependencies
|