@toa.io/norm 1.0.0-alpha.284 → 1.0.0-alpha.287
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 +1 -196
- package/package.json +5 -5
- package/src/.component/.expand/bridge.js +1 -1
- package/src/.component/.expand/entity.js +1 -1
- package/src/.component/.expand/events.js +1 -1
- package/src/.component/.expand/extensions.js +1 -1
- package/src/.component/.expand/operations.js +1 -1
- package/src/.component/.expand/properties.js +1 -1
- package/src/.component/.expand/receivers.js +3 -2
- package/src/.component/.expand/version.js +11 -13
- package/src/.component/.normalize/entity.js +7 -0
- package/src/.component/.normalize/events.js +4 -1
- package/src/.component/.normalize/index.js +1 -0
- package/src/.component/.normalize/operations.js +9 -1
- package/src/.component/.normalize/receivers.js +4 -1
- package/src/.component/collapse.js +30 -6
- package/src/.component/defaults.js +3 -4
- package/src/.component/dependencies.js +7 -4
- package/src/.component/dereference.js +14 -13
- package/src/.component/expand.js +11 -2
- package/src/.component/extensions.js +2 -1
- package/src/.component/index.js +1 -0
- package/src/.component/merge.js +3 -5
- package/src/.component/migrations.js +57 -0
- package/src/.component/normalize.js +9 -1
- package/src/.component/schema.yaml +29 -39
- package/src/.component/validate.js +23 -3
- package/src/.context/.dependencies/connectors.js +3 -3
- package/src/.context/.dependencies/describe.js +4 -1
- package/src/.context/.dependencies/extensions.js +18 -13
- package/src/.context/.dependencies/load.js +6 -4
- package/src/.context/.dependencies/resolve.js +1 -1
- package/src/.context/dereference.js +3 -1
- package/src/.context/normalize.js +5 -0
- package/src/.context/schema.yaml +6 -4
- package/src/component.js +14 -5
- package/src/context.js +9 -5
- package/src/entity.js +15 -0
- package/src/index.js +1 -0
- package/src/shortcuts.js +0 -1
- package/test/component/collapse.fixtures.js +28 -34
- package/test/component/collapse.test.js +41 -0
- package/test/component/dependencies.test.js +6 -6
- package/test/component/dereference.fixtures.js +13 -17
- package/test/component/dereference.test.js +12 -3
- 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/migrations.test.js +62 -0
- package/test/component/normalize.test.js +61 -13
- package/test/component/validate.fixtures.js +14 -8
- package/test/component/validate.test.js +113 -59
- package/test/context/complete.test.js +5 -1
- package/test/context/dereference.test.js +3 -2
- package/test/context/expand.test.js +4 -2
- package/test/context/normalize.test.js +14 -1
- package/test/context/validate.test.js +40 -20
- package/test/shortcuts.test.js +2 -1
- package/types/component.d.ts +14 -3
- package/types/context/declaration.d.ts +1 -1
- package/types/context.d.ts +10 -9
- package/types/entity.d.ts +7 -0
- package/types/index.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,201 +3,6 @@
|
|
|
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.
|
|
7
|
-
|
|
8
|
-
### Features
|
|
9
|
-
|
|
10
|
-
* an operation states what it is ([b5e2f66](https://github.com/toa-io/toa/commit/b5e2f66c8bf67924e2eaaaa11f283dfb4d810981))
|
|
11
|
-
* **cadence:** calls a component makes to itself, and calls it puts off ([8bbde5f](https://github.com/toa-io/toa/commit/8bbde5fc89e8e5549d5aa2bfcddb1beec0871565))
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# [1.0.0-alpha.283](https://github.com/toa-io/toa/compare/v1.0.0-alpha.282...v1.0.0-alpha.283) (2026-09-04)
|
|
15
|
-
|
|
16
|
-
### Bug Fixes
|
|
17
|
-
|
|
18
|
-
* **exposition:** declare the errors the built-in components return ([27200c3](https://github.com/toa-io/toa/commit/27200c348ab09068d58c52c3e83208cb29cd7290))
|
|
19
|
-
|
|
20
|
-
### Features
|
|
21
|
-
|
|
22
|
-
* **operations:** run extension services inside a composition ([154aced](https://github.com/toa-io/toa/commit/154aced047228a250ac3bab353e09432fcc5a34d))
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# [1.0.0-alpha.282](https://github.com/toa-io/toa/compare/v1.0.0-alpha.281...v1.0.0-alpha.282) (2026-09-03)
|
|
26
|
-
|
|
27
|
-
**Note:** Version bump only for package @toa.io/norm
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# [1.0.0-alpha.278](https://github.com/toa-io/toa/compare/v1.0.0-alpha.277...v1.0.0-alpha.278) (2026-09-03)
|
|
34
|
-
|
|
35
|
-
**Note:** Version bump only for package @toa.io/norm
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# [1.0.0-alpha.277](https://github.com/toa-io/toa/compare/v1.0.0-alpha.276...v1.0.0-alpha.277) (2026-09-03)
|
|
42
|
-
|
|
43
|
-
### Bug Fixes
|
|
44
|
-
|
|
45
|
-
* read a manifest the way js-yaml read one before ([fcea1b2](https://github.com/toa-io/toa/commit/fcea1b21996d6efa6c3198acbfc9f70f1ce0c35c))
|
|
46
|
-
* settle what the module loader made asynchronous ([d4ab9df](https://github.com/toa-io/toa/commit/d4ab9dfb09e382da311dba08f52214e7645f4a6c))
|
|
47
|
-
* what the loader resolves, and what it will not ([1b8a02f](https://github.com/toa-io/toa/commit/1b8a02ff0a5e0f18ee62e3f71bc81116f86f9af1))
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# [1.0.0-alpha.274](https://github.com/toa-io/toa/compare/v1.0.0-alpha.273...v1.0.0-alpha.274) (2026-09-02)
|
|
51
|
-
|
|
52
|
-
**Note:** Version bump only for package @toa.io/norm
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# [1.0.0-alpha.273](https://github.com/toa-io/toa/compare/v1.0.0-alpha.272...v1.0.0-alpha.273) (2026-09-02)
|
|
59
|
-
|
|
60
|
-
**Note:** Version bump only for package @toa.io/norm
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# [1.0.0-alpha.272](https://github.com/toa-io/toa/compare/v1.0.0-alpha.271...v1.0.0-alpha.272) (2026-09-01)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
### Bug Fixes
|
|
70
|
-
|
|
71
|
-
* **atomicity:** honour a cluster, and let it be unreachable ([c475d66](https://github.com/toa-io/toa/commit/c475d663ddf7329d86da517ccceee4cb03280e95))
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
* feat(atomicity)!: take a quorum of independent servers ([862562f](https://github.com/toa-io/toa/commit/862562f24d77ec00127dbbd88d43802b208c643b))
|
|
75
|
-
* refactor(atomicity)!: take one Redis, not a cluster ([b0a4770](https://github.com/toa-io/toa/commit/b0a4770dff5dfe8fee4b9ae637a4dd297454fcd4))
|
|
76
|
-
* refactor(atomicity)!: take the lock manager from the stash ([4fd91eb](https://github.com/toa-io/toa/commit/4fd91eb2fdb68f7b87aaf16d182794bbc567ba57))
|
|
77
|
-
* refactor(core)!: pump the outbox in one cycle ([bf590fe](https://github.com/toa-io/toa/commit/bf590fe8ed59c701b5fd1a91ba4874685b79242f))
|
|
78
|
-
* refactor(atomicity)!: rename the connector and free it of the outbox ([21f1a41](https://github.com/toa-io/toa/commit/21f1a41aceafcfd35c7620014062fe46b54afa95))
|
|
79
|
-
* feat(core)!: commit events with the state that produced them ([1eb68cc](https://github.com/toa-io/toa/commit/1eb68cc435dbfa03faa16009fceb866693d22e1a)), closes [#20](https://github.com/toa-io/toa/issues/20)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
### Features
|
|
83
|
-
|
|
84
|
-
* **atomicity:** make the interval a setting, and stop repeating n-and-i ([5c3a6d1](https://github.com/toa-io/toa/commit/5c3a6d1533751a21b7eb7d9c737f1a270bf5a5ae))
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
### BREAKING CHANGES
|
|
88
|
-
|
|
89
|
-
* `atomicity.redis` accepts a list again, of independent servers rather
|
|
90
|
-
than cluster nodes, and refuses an even number of them.
|
|
91
|
-
|
|
92
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
93
|
-
* `atomicity.redis` is a string. A list is refused at export.
|
|
94
|
-
|
|
95
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
96
|
-
* `context.stash.lock` is gone; lock through `context.atom` instead.
|
|
97
|
-
A stash pointer resolving to several addresses now uses the first.
|
|
98
|
-
|
|
99
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
100
|
-
* `Storage.outbox.pending` takes a fourth argument, the id to
|
|
101
|
-
continue from.
|
|
102
|
-
|
|
103
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
104
|
-
* `@toa.io/partitions.redis` is now `@toa.io/atomicity`, and its
|
|
105
|
-
`lanes(total)` is `slots(total)`. Redis is declared as `atomicity` in the context
|
|
106
|
-
rather than `outbox.redis`, and read from `TOA_ATOMICITY_REDIS`.
|
|
107
|
-
`TOA_OUTBOX_PARTITION_INTERVAL` is `TOA_ATOMICITY_INTERVAL`.
|
|
108
|
-
|
|
109
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
110
|
-
* `event.changeset` is removed; use `origin` and `state`. `State`
|
|
111
|
-
takes an `Outbox` in place of an `Emission`. `difference` is dropped from
|
|
112
|
-
`@toa.io/generic` along with its last caller.
|
|
113
|
-
|
|
114
|
-
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# [1.0.0-alpha.270](https://github.com/toa-io/toa/compare/v1.0.0-alpha.269...v1.0.0-alpha.270) (2026-08-31)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
### Bug Fixes
|
|
124
|
-
|
|
125
|
-
* **norm:** resolve annotations keyed by a dependency id ([e2860cd](https://github.com/toa-io/toa/commit/e2860cd2cc93dd3a135c092db1d943f80648ee56))
|
|
126
|
-
* **norm:** return the hash after hashing a file ([35058bd](https://github.com/toa-io/toa/commit/35058bdaae19f367197ea5b800742349e507ab11))
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
### Performance Improvements
|
|
130
|
-
|
|
131
|
-
* **boot:** stop paying for what a composition does not need to start ([1619c27](https://github.com/toa-io/toa/commit/1619c2743072f4706939ce72f3074e615d26a91e))
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
# [1.0.0-alpha.266](https://github.com/toa-io/toa/compare/v1.0.0-alpha.265...v1.0.0-alpha.266) (2026-08-29)
|
|
138
|
-
|
|
139
|
-
**Note:** Version bump only for package @toa.io/norm
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
# [1.0.0-alpha.265](https://github.com/toa-io/toa/compare/v1.0.0-alpha.264...v1.0.0-alpha.265) (2026-08-29)
|
|
146
|
-
|
|
147
|
-
**Note:** Version bump only for package @toa.io/norm
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
# [1.0.0-alpha.264](https://github.com/toa-io/toa/compare/v1.0.0-alpha.263...v1.0.0-alpha.264) (2026-08-29)
|
|
6
|
+
# [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)
|
|
154
7
|
|
|
155
8
|
**Note:** Version bump only for package @toa.io/norm
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
# [1.0.0-alpha.263](https://github.com/toa-io/toa/compare/v1.0.0-alpha.262...v1.0.0-alpha.263) (2026-08-29)
|
|
162
|
-
|
|
163
|
-
**Note:** Version bump only for package @toa.io/norm
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
# [1.0.0-alpha.262](https://github.com/toa-io/toa/compare/v1.0.0-alpha.261...v1.0.0-alpha.262) (2026-08-28)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
### Features
|
|
173
|
-
|
|
174
|
-
* **introspection:** add the introspection extension ([a07ed86](https://github.com/toa-io/toa/commit/a07ed8651404258d544f3b6d8236e58cf4da09ac))
|
|
175
|
-
* **operations:** let a service claim a path prefix on the host ([02677d1](https://github.com/toa-io/toa/commit/02677d17fcd4222b07c3be458d93bcc496a016a8))
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
# [1.0.0-alpha.259](https://github.com/toa-io/toa/compare/v1.0.0-alpha.258...v1.0.0-alpha.259) (2026-08-24)
|
|
182
|
-
|
|
183
|
-
**Note:** Version bump only for package @toa.io/norm
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
# [1.0.0-alpha.257](https://github.com/toa-io/toa/compare/v1.0.0-alpha.256...v1.0.0-alpha.257) (2026-08-24)
|
|
190
|
-
|
|
191
|
-
**Note:** Version bump only for package @toa.io/norm
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
# [1.0.0-alpha.256](https://github.com/toa-io/toa/compare/v1.0.0-alpha.255...v1.0.0-alpha.256) (2026-08-23)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
### Features
|
|
201
|
-
|
|
202
|
-
* add toa mono and discover components at components/* ([787a111](https://github.com/toa-io/toa/commit/787a111c8f83e56ebc734801c97106c585d034ea))
|
|
203
|
-
* deploy a single mono image with toa deploy --mono ([a7f14f9](https://github.com/toa-io/toa/commit/a7f14f9877a280e9c74d16c195028b35d74fb15a))
|
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.287",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Toa declarations normalization and validation",
|
|
6
6
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
@@ -21,11 +21,11 @@
|
|
|
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/generic": "1.0.0-alpha.
|
|
26
|
-
"@toa.io/schemas": "1.0.0-alpha.
|
|
24
|
+
"@toa.io/core": "1.0.0-alpha.287",
|
|
25
|
+
"@toa.io/generic": "1.0.0-alpha.286",
|
|
26
|
+
"@toa.io/schemas": "1.0.0-alpha.287",
|
|
27
27
|
"fast-glob": "3.3.3",
|
|
28
28
|
"js-yaml": "5.4.1"
|
|
29
29
|
},
|
|
30
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "5c7944a70c16065ab6372072385cc1f02f5db77c"
|
|
31
31
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { resolve } from '../../shortcuts.js'
|
|
2
2
|
|
|
3
|
-
export function receivers
|
|
3
|
+
export function receivers(manifest) {
|
|
4
4
|
if (manifest.receivers === undefined) return
|
|
5
5
|
|
|
6
6
|
for (const [locator, receiver] of Object.entries(manifest.receivers)) {
|
|
7
|
-
if (typeof receiver === 'string')
|
|
7
|
+
if (typeof receiver === 'string')
|
|
8
|
+
manifest.receivers[locator] = { operation: receiver }
|
|
8
9
|
|
|
9
10
|
if (receiver.binding !== undefined) receiver.binding = resolve(receiver.binding)
|
|
10
11
|
if (receiver.bridge !== undefined) receiver.bridge = resolve(receiver.bridge)
|
|
@@ -2,7 +2,7 @@ import { join } from 'node:path'
|
|
|
2
2
|
import { createHash } from 'node:crypto'
|
|
3
3
|
import fs from 'node:fs/promises'
|
|
4
4
|
|
|
5
|
-
export async function version
|
|
5
|
+
export async function version(manifest) {
|
|
6
6
|
manifest.version ??= await hash(manifest.path)
|
|
7
7
|
}
|
|
8
8
|
|
|
@@ -14,14 +14,13 @@ export async function version (manifest) {
|
|
|
14
14
|
* build writes (see `operations/src/deployment/images/image.js`), so hashing it would only
|
|
15
15
|
* retag an identical image after a local install.
|
|
16
16
|
*/
|
|
17
|
-
async function hash
|
|
17
|
+
async function hash(path) {
|
|
18
18
|
const files = (await list(path)).sort()
|
|
19
19
|
const digests = await Promise.all(files.map((file) => digest(join(path, file))))
|
|
20
20
|
const total = createHash('sha256')
|
|
21
21
|
|
|
22
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++)
|
|
24
|
-
total.update(files[i]).update(digests[i])
|
|
23
|
+
for (let i = 0; i < files.length; i++) total.update(files[i]).update(digests[i])
|
|
25
24
|
|
|
26
25
|
return total.digest('hex').slice(0, 8)
|
|
27
26
|
}
|
|
@@ -32,26 +31,25 @@ async function hash (path) {
|
|
|
32
31
|
* @param {string[]} [acc]
|
|
33
32
|
* @returns {Promise<string[]>} paths relative to `root`
|
|
34
33
|
*/
|
|
35
|
-
async function list
|
|
34
|
+
async function list(root, path = '', acc = []) {
|
|
36
35
|
const entries = await fs.readdir(join(root, path), { withFileTypes: true })
|
|
37
36
|
|
|
38
37
|
for (const entry of entries) {
|
|
39
|
-
if (EXCLUDED.has(entry.name))
|
|
40
|
-
continue
|
|
38
|
+
if (EXCLUDED.has(entry.name)) continue
|
|
41
39
|
|
|
42
40
|
const relative = path === '' ? entry.name : `${path}/${entry.name}`
|
|
43
41
|
|
|
44
|
-
if (entry.isDirectory())
|
|
45
|
-
|
|
46
|
-
else if (entry.isFile())
|
|
47
|
-
acc.push(relative)
|
|
42
|
+
if (entry.isDirectory()) await list(root, relative, acc)
|
|
43
|
+
else if (entry.isFile()) acc.push(relative)
|
|
48
44
|
}
|
|
49
45
|
|
|
50
46
|
return acc
|
|
51
47
|
}
|
|
52
48
|
|
|
53
|
-
async function digest
|
|
54
|
-
return createHash('sha256')
|
|
49
|
+
async function digest(path) {
|
|
50
|
+
return createHash('sha256')
|
|
51
|
+
.update(await fs.readFile(path))
|
|
52
|
+
.digest()
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
const EXCLUDED = new Set(['node_modules', '.git'])
|
|
@@ -5,11 +5,14 @@ export const events = async (component) => {
|
|
|
5
5
|
|
|
6
6
|
for (const event of Object.values(component.events)) {
|
|
7
7
|
if (event.binding === undefined) event.binding = binding
|
|
8
|
+
|
|
9
|
+
event.conditioned ??= false
|
|
10
|
+
event.subjective ??= false
|
|
8
11
|
}
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
/** The first binding that carries events, which only its module can say. */
|
|
12
|
-
async function asynchronous
|
|
15
|
+
async function asynchronous(bindings) {
|
|
13
16
|
for (const binding of bindings) {
|
|
14
17
|
const { properties } = await import(binding)
|
|
15
18
|
|
|
@@ -2,8 +2,16 @@ export const operations = (component) => {
|
|
|
2
2
|
if (component.operations === undefined) return
|
|
3
3
|
|
|
4
4
|
for (const [endpoint, operation] of Object.entries(component.operations)) {
|
|
5
|
-
|
|
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'
|
|
6
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 ??= {}
|
|
7
15
|
|
|
8
16
|
if (operation.bindings === undefined) operation.bindings = component.bindings
|
|
9
17
|
if (operation.bindings === null) operation.bindings = []
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function receivers
|
|
1
|
+
export function receivers(component) {
|
|
2
2
|
if (component.receivers === undefined) return
|
|
3
3
|
|
|
4
4
|
const receivers = component.receivers
|
|
@@ -7,6 +7,9 @@ export function receivers (component) {
|
|
|
7
7
|
const segments = key.split('.')
|
|
8
8
|
const source = value.source ?? 'default'
|
|
9
9
|
|
|
10
|
+
value.conditioned ??= false
|
|
11
|
+
value.adaptive ??= false
|
|
12
|
+
|
|
10
13
|
if (source !== 'default') continue
|
|
11
14
|
if (segments.length === 3) continue // already with a namespace
|
|
12
15
|
|
|
@@ -12,7 +12,8 @@ export const collapse = (manifest, prototype) => {
|
|
|
12
12
|
if (manifest.operations === undefined) manifest.operations = {}
|
|
13
13
|
|
|
14
14
|
for (let [endpoint, operation] of operations) {
|
|
15
|
-
if (manifest.operations[endpoint] === undefined)
|
|
15
|
+
if (manifest.operations[endpoint] === undefined)
|
|
16
|
+
manifest.operations[endpoint] = {}
|
|
16
17
|
else {
|
|
17
18
|
const { virtual, ...real } = operation
|
|
18
19
|
|
|
@@ -24,7 +25,8 @@ export const collapse = (manifest, prototype) => {
|
|
|
24
25
|
merge(manifest.operations[endpoint], declaration)
|
|
25
26
|
|
|
26
27
|
if (bridge !== undefined) {
|
|
27
|
-
if (manifest.prototype.operations === undefined)
|
|
28
|
+
if (manifest.prototype.operations === undefined)
|
|
29
|
+
manifest.prototype.operations = {}
|
|
28
30
|
|
|
29
31
|
manifest.prototype.operations[endpoint] = { bridge }
|
|
30
32
|
}
|
|
@@ -34,16 +36,38 @@ export const collapse = (manifest, prototype) => {
|
|
|
34
36
|
|
|
35
37
|
const { entity, events, extensions } = prototype
|
|
36
38
|
|
|
37
|
-
if (
|
|
39
|
+
if (
|
|
40
|
+
manifest.entity?.properties?.id !== undefined &&
|
|
41
|
+
entity?.properties?.id !== undefined
|
|
42
|
+
) {
|
|
38
43
|
manifest.entity.custom = true
|
|
39
44
|
|
|
40
|
-
delete prototype.entity.
|
|
45
|
+
delete prototype.entity.properties.id
|
|
41
46
|
}
|
|
42
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
|
+
|
|
43
61
|
if (prototype.events !== undefined && manifest.events !== undefined)
|
|
44
62
|
for (const event of Object.keys(prototype.events))
|
|
45
|
-
if (event in manifest.events)
|
|
46
|
-
|
|
63
|
+
if (event in manifest.events) delete prototype.events[event]
|
|
64
|
+
|
|
65
|
+
// a migration is applied to a collection, and a prototype has none: what it declares belongs
|
|
66
|
+
// to the component that declared it, so it is not collapsed into this one
|
|
67
|
+
if (entity?.migrations !== undefined) delete entity.migrations
|
|
47
68
|
|
|
48
69
|
merge(manifest, { entity, events, extensions })
|
|
49
70
|
}
|
|
71
|
+
|
|
72
|
+
/** What the runtime writes into every record. `id` is not among them: a component may own it. */
|
|
73
|
+
const SYSTEM = ['VERSION', 'CREATED', 'UPDATED', 'DELETED']
|
|
@@ -13,18 +13,17 @@ export const defaults = (manifest, proto) => {
|
|
|
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
20
|
}
|
|
22
21
|
|
|
23
|
-
function protoName
|
|
22
|
+
function protoName(manifest) {
|
|
24
23
|
return 'proto' + hash(manifest.path)
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
function nameAfterDir
|
|
26
|
+
function nameAfterDir(manifest) {
|
|
28
27
|
const parts = manifest.path.split('/')
|
|
29
28
|
const dirname = parts[parts.length - 1]
|
|
30
29
|
const [name, namespace] = dirname.split('.').reverse()
|
|
@@ -9,20 +9,23 @@ const require = createRequire(import.meta.url)
|
|
|
9
9
|
* @param {toa.norm.Component} component
|
|
10
10
|
*/
|
|
11
11
|
export const dependencies = (component) => {
|
|
12
|
-
if ('entity' in component)
|
|
12
|
+
if ('entity' in component)
|
|
13
|
+
component.entity.storage = resolve(component.path, component.entity.storage)
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
function resolve
|
|
16
|
+
function resolve(root, reference) {
|
|
16
17
|
const paths = [root, import.meta.dirname]
|
|
17
18
|
const options = { paths }
|
|
18
19
|
|
|
19
20
|
let path
|
|
20
21
|
|
|
21
|
-
try {
|
|
22
|
+
try {
|
|
23
|
+
// as package
|
|
22
24
|
const packageJsonRef = join(reference, 'package.json')
|
|
23
25
|
|
|
24
26
|
path = require.resolve(packageJsonRef, options)
|
|
25
|
-
} catch {
|
|
27
|
+
} catch {
|
|
28
|
+
// as directory
|
|
26
29
|
path = require.resolve(reference, options)
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -2,14 +2,14 @@ import { merge } from '@toa.io/generic'
|
|
|
2
2
|
|
|
3
3
|
export const dereference = (manifest) => {
|
|
4
4
|
// schemas
|
|
5
|
-
const
|
|
5
|
+
const properties = manifest.entity?.properties
|
|
6
|
+
const resolver = createResolver(properties)
|
|
6
7
|
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if ('operations' in manifest)
|
|
11
|
-
operations(manifest, resolver)
|
|
8
|
+
if (properties !== undefined)
|
|
9
|
+
for (const [name, property] of Object.entries(properties))
|
|
10
|
+
properties[name] = schema(property, resolver)
|
|
12
11
|
|
|
12
|
+
if ('operations' in manifest) operations(manifest, resolver)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const createResolver = (properties) => (property) => {
|
|
@@ -20,15 +20,14 @@ const createResolver = (properties) => (property) => {
|
|
|
20
20
|
return properties[property]
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function operations
|
|
23
|
+
function operations(manifest, resolver) {
|
|
24
24
|
for (const operation of Object.values(manifest.operations)) {
|
|
25
25
|
if (operation.input !== undefined) operation.input = schema(operation.input, resolver)
|
|
26
26
|
|
|
27
27
|
if (operation.output !== undefined)
|
|
28
28
|
if (Array.isArray(operation.output) && operation.output.length === 1)
|
|
29
29
|
operation.output = [schema(operation.output[0], resolver)]
|
|
30
|
-
else
|
|
31
|
-
operation.output = schema(operation.output, resolver)
|
|
30
|
+
else operation.output = schema(operation.output, resolver)
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
// forwarding
|
|
@@ -39,18 +38,19 @@ function operations (manifest, resolver) {
|
|
|
39
38
|
for (const operation of Object.values(manifest.operations)) {
|
|
40
39
|
delete operation.forwarded
|
|
41
40
|
}
|
|
42
|
-
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
const schema = (object, resolve) => {
|
|
46
44
|
if (object === undefined || object === null || typeof object !== 'object') return
|
|
47
|
-
if (object.type === 'string' && object.default?.[0] === '.')
|
|
45
|
+
if (object.type === 'string' && object.default?.[0] === '.')
|
|
46
|
+
return resolve(object.default.substring(1))
|
|
48
47
|
|
|
49
48
|
if (object.type === 'array') {
|
|
50
49
|
object.items = schema(object.items, resolve)
|
|
51
50
|
} else if (object.properties !== undefined) {
|
|
52
51
|
for (const [name, value] of Object.entries(object.properties)) {
|
|
53
|
-
if (value?.type === 'string' && value.default === '.')
|
|
52
|
+
if (value?.type === 'string' && value.default === '.')
|
|
53
|
+
object.properties[name] = resolve(name)
|
|
54
54
|
else object.properties[name] = schema(value, resolve)
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -61,7 +61,8 @@ const schema = (object, resolve) => {
|
|
|
61
61
|
const forward = (operation, operations) => {
|
|
62
62
|
const target = operations[operation.forward]
|
|
63
63
|
|
|
64
|
-
if (target === undefined)
|
|
64
|
+
if (target === undefined)
|
|
65
|
+
throw new Error(`Referenced operation '${operation.forward}' is not defined`)
|
|
65
66
|
|
|
66
67
|
if (target.forward !== undefined) {
|
|
67
68
|
if (target.forwarded !== true) forward(target, operations)
|
package/src/.component/expand.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
entity,
|
|
3
|
+
bridge,
|
|
4
|
+
operations,
|
|
5
|
+
events,
|
|
6
|
+
receivers,
|
|
7
|
+
extensions,
|
|
8
|
+
properties,
|
|
9
|
+
version
|
|
10
|
+
} from './.expand/index.js'
|
|
2
11
|
|
|
3
|
-
export async function expand
|
|
12
|
+
export async function expand(manifest) {
|
|
4
13
|
entity(manifest)
|
|
5
14
|
bridge(manifest)
|
|
6
15
|
operations(manifest)
|
|
@@ -21,7 +21,8 @@ export const extensions = async (manifest) => {
|
|
|
21
21
|
if (extension.manifest !== undefined) {
|
|
22
22
|
declaration = extension.manifest(declaration, manifest)
|
|
23
23
|
|
|
24
|
-
if (declaration === undefined)
|
|
24
|
+
if (declaration === undefined)
|
|
25
|
+
throw new Error(`Extension '${reference}' hasn't returned manifest`)
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
extensions[key] = declaration
|
package/src/.component/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { dependencies } from './dependencies.js'
|
|
|
4
4
|
export { dereference } from './dereference.js'
|
|
5
5
|
export { expand } from './expand.js'
|
|
6
6
|
export { merge } from './merge.js'
|
|
7
|
+
export { migrations } from './migrations.js'
|
|
7
8
|
export { normalize } from './normalize.js'
|
|
8
9
|
export { validate } from './validate.js'
|
|
9
10
|
export { extensions } from './extensions.js'
|