@toa.io/norm 1.0.0-alpha.31 → 1.0.0-alpha.310
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 +208 -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 +6 -8
- package/src/.component/.expand/index.js +7 -19
- package/src/.component/.expand/operations.js +2 -9
- package/src/.component/.expand/receivers.js +4 -7
- package/src/.component/.expand/version.js +67 -28
- 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 +13 -6
- package/src/.component/.normalize/receivers.js +4 -5
- package/src/.component/collapse.js +60 -9
- package/src/.component/defaults.js +5 -11
- package/src/.component/dependencies.js +14 -10
- package/src/.component/dereference.js +5 -49
- package/src/.component/expand.js +3 -9
- package/src/.component/extensions.js +9 -10
- package/src/.component/index.js +10 -21
- package/src/.component/merge.js +25 -15
- package/src/.component/migrations.js +57 -0
- package/src/.component/normalize.js +10 -6
- package/src/.component/schema.yaml +147 -73
- package/src/.component/validate.js +37 -17
- package/src/.context/.dependencies/connectors.js +5 -9
- 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 +181 -11
- package/src/.context/validate.js +8 -14
- package/src/component.js +33 -14
- package/src/context.js +44 -15
- package/src/definition.js +56 -0
- package/src/entity.js +15 -0
- package/src/index.js +7 -9
- package/src/manifest.js +154 -0
- package/src/map.js +24 -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 +10 -98
- package/test/component/dereference.test.js +10 -14
- 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 -13
- 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 +94 -28
- package/test/component/validate.fixtures.js +15 -13
- package/test/component/validate.test.js +180 -109
- 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 +158 -35
- 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 +27 -4
- package/types/context/declaration.d.ts +34 -3
- package/types/context.d.ts +27 -13
- package/types/entity.d.ts +7 -0
- package/types/index.d.ts +18 -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
|
@@ -8,7 +8,7 @@ definitions:
|
|
|
8
8
|
binding:
|
|
9
9
|
type: string
|
|
10
10
|
not:
|
|
11
|
-
const:
|
|
11
|
+
const: "@toa.io/bindings.loop" # loop is for system use only
|
|
12
12
|
|
|
13
13
|
type: object
|
|
14
14
|
properties:
|
|
@@ -17,40 +17,55 @@ properties:
|
|
|
17
17
|
nullable: true
|
|
18
18
|
properties:
|
|
19
19
|
prototype:
|
|
20
|
-
$ref:
|
|
20
|
+
$ref: "#/properties/prototype"
|
|
21
21
|
path:
|
|
22
22
|
type: string
|
|
23
23
|
operations:
|
|
24
24
|
type: object
|
|
25
25
|
propertyNames:
|
|
26
|
-
$ref:
|
|
26
|
+
$ref: "#/definitions/name"
|
|
27
27
|
patternProperties:
|
|
28
|
-
|
|
28
|
+
".*":
|
|
29
29
|
type: object
|
|
30
30
|
properties:
|
|
31
31
|
bridge:
|
|
32
|
-
$ref:
|
|
32
|
+
$ref: "#/properties/bridge"
|
|
33
33
|
|
|
34
34
|
path:
|
|
35
35
|
type: string
|
|
36
36
|
|
|
37
|
+
files:
|
|
38
|
+
# what this component's version is made of, where a build of it copies only some of it
|
|
39
|
+
type: array
|
|
40
|
+
items:
|
|
41
|
+
type: string
|
|
42
|
+
|
|
43
|
+
ignore:
|
|
44
|
+
# what its version is not made of, added to what the bridge states; `!` puts one of those back
|
|
45
|
+
type: array
|
|
46
|
+
items:
|
|
47
|
+
type: string
|
|
48
|
+
|
|
37
49
|
locator:
|
|
38
50
|
type: object
|
|
39
51
|
properties:
|
|
40
52
|
id:
|
|
41
|
-
|
|
53
|
+
type: string
|
|
54
|
+
pattern: ^([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?)(\.([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?))$
|
|
42
55
|
label:
|
|
43
|
-
|
|
56
|
+
type: string
|
|
57
|
+
pattern: ^([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?)(-([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?))*$
|
|
44
58
|
|
|
45
59
|
name:
|
|
46
|
-
|
|
60
|
+
type: string
|
|
61
|
+
pattern: ^[a-zA-Z]([a-zA-Z0-9]{1,31})?$
|
|
47
62
|
|
|
48
63
|
namespace:
|
|
49
|
-
|
|
50
|
-
|
|
64
|
+
type: string
|
|
65
|
+
pattern: ^[a-zA-Z]([a-zA-Z0-9]{1,31})?$
|
|
51
66
|
not:
|
|
52
67
|
oneOf:
|
|
53
|
-
- const:
|
|
68
|
+
- const: "system"
|
|
54
69
|
|
|
55
70
|
version:
|
|
56
71
|
type: string
|
|
@@ -60,56 +75,60 @@ properties:
|
|
|
60
75
|
properties:
|
|
61
76
|
image:
|
|
62
77
|
type: string
|
|
78
|
+
run:
|
|
79
|
+
type: string
|
|
63
80
|
|
|
64
81
|
entity:
|
|
65
82
|
type: object
|
|
66
83
|
properties:
|
|
67
84
|
storage:
|
|
68
85
|
type: string
|
|
69
|
-
|
|
70
|
-
schema:
|
|
71
|
-
$ref: 'definitions#/definitions/schema'
|
|
86
|
+
properties:
|
|
72
87
|
type: object
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
# a system property is named like any other, so nothing is enumerated beside them
|
|
89
|
+
propertyNames:
|
|
90
|
+
$ref: "#/definitions/name"
|
|
91
|
+
additionalProperties:
|
|
92
|
+
$ref: https://json-schema.org/draft/2019-09/schema
|
|
93
|
+
required:
|
|
94
|
+
type: array
|
|
95
|
+
uniqueItems: true
|
|
96
|
+
items:
|
|
97
|
+
$ref: "#/definitions/name"
|
|
98
|
+
blank:
|
|
99
|
+
# what a record holds before anything is written to it; a system property is the
|
|
100
|
+
# runtime's to write, so none may be named here
|
|
84
101
|
type: object
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
102
|
+
propertyNames:
|
|
103
|
+
not:
|
|
104
|
+
enum: [id, VERSION, CREATED, UPDATED, DELETED, REGION]
|
|
105
|
+
migrations:
|
|
106
|
+
type: array
|
|
107
|
+
items:
|
|
108
|
+
type: object
|
|
109
|
+
properties:
|
|
110
|
+
id:
|
|
89
111
|
type: string
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
enum: [asc, desc, hash]
|
|
112
|
+
# what a step means belongs to the storage, which is the only thing that reads it
|
|
113
|
+
steps:
|
|
114
|
+
type: array
|
|
115
|
+
# the prototype it is inherited from; absent on the component's own
|
|
116
|
+
prototype:
|
|
117
|
+
type: string
|
|
118
|
+
required: [id, steps]
|
|
119
|
+
additionalProperties: false
|
|
99
120
|
associated:
|
|
100
121
|
type: boolean
|
|
101
|
-
default: false
|
|
102
122
|
custom:
|
|
103
123
|
type: boolean
|
|
104
|
-
|
|
105
|
-
required: [schema]
|
|
124
|
+
required: [properties]
|
|
106
125
|
additionalProperties: false
|
|
107
126
|
|
|
108
127
|
bindings:
|
|
109
128
|
type: array
|
|
110
129
|
uniqueItems: true
|
|
111
130
|
items:
|
|
112
|
-
$ref:
|
|
131
|
+
$ref: "#/definitions/binding"
|
|
113
132
|
|
|
114
133
|
bridge:
|
|
115
134
|
type: string
|
|
@@ -117,27 +136,49 @@ properties:
|
|
|
117
136
|
operations:
|
|
118
137
|
type: object
|
|
119
138
|
propertyNames:
|
|
120
|
-
$ref:
|
|
139
|
+
$ref: "#/definitions/name"
|
|
121
140
|
patternProperties:
|
|
122
|
-
|
|
141
|
+
".*":
|
|
123
142
|
type: object
|
|
124
143
|
properties:
|
|
125
144
|
type:
|
|
126
|
-
enum:
|
|
145
|
+
enum:
|
|
146
|
+
[
|
|
147
|
+
transition,
|
|
148
|
+
observation,
|
|
149
|
+
assignment,
|
|
150
|
+
computation,
|
|
151
|
+
effect,
|
|
152
|
+
unmanaged,
|
|
153
|
+
]
|
|
127
154
|
scope:
|
|
128
|
-
enum: [object, objects, changeset, none]
|
|
155
|
+
enum: [object, objects, changeset, stream, none]
|
|
129
156
|
concurrency:
|
|
130
157
|
enum: [none, retry]
|
|
158
|
+
once:
|
|
159
|
+
# the same call arriving twice changes state once; see documentation/inbox.md
|
|
160
|
+
type: boolean
|
|
161
|
+
stateful:
|
|
162
|
+
# served by every process under an address of its own, which a call names; see documentation/stateful.md
|
|
163
|
+
type: boolean
|
|
131
164
|
forward:
|
|
132
|
-
$ref:
|
|
165
|
+
$ref: "#/definitions/name"
|
|
133
166
|
bridge:
|
|
134
167
|
type: string
|
|
135
168
|
bindings:
|
|
136
|
-
$ref:
|
|
169
|
+
$ref: "#/properties/bindings"
|
|
170
|
+
description:
|
|
171
|
+
# what the operation is, for whoever reads it: a generated type, an OPTIONS, a model
|
|
172
|
+
type: string
|
|
137
173
|
input:
|
|
138
|
-
$ref:
|
|
174
|
+
$ref: https://json-schema.org/draft/2019-09/schema
|
|
139
175
|
output:
|
|
140
|
-
$ref:
|
|
176
|
+
$ref: https://json-schema.org/draft/2019-09/schema
|
|
177
|
+
errors:
|
|
178
|
+
# what a reply may carry as a code: the error schema allows either
|
|
179
|
+
type: array
|
|
180
|
+
items:
|
|
181
|
+
type: [string, integer]
|
|
141
182
|
query:
|
|
142
183
|
type: boolean
|
|
143
184
|
required: [type, scope, bindings]
|
|
@@ -149,8 +190,8 @@ properties:
|
|
|
149
190
|
then:
|
|
150
191
|
properties:
|
|
151
192
|
scope:
|
|
152
|
-
enum: [object]
|
|
153
|
-
if: #
|
|
193
|
+
enum: [object, objects]
|
|
194
|
+
if: # not query: false
|
|
154
195
|
not:
|
|
155
196
|
properties:
|
|
156
197
|
query:
|
|
@@ -158,6 +199,17 @@ properties:
|
|
|
158
199
|
required: [query]
|
|
159
200
|
then:
|
|
160
201
|
required: [concurrency]
|
|
202
|
+
- if: # transition and scope=objects
|
|
203
|
+
properties:
|
|
204
|
+
type:
|
|
205
|
+
const: transition
|
|
206
|
+
scope:
|
|
207
|
+
const: objects
|
|
208
|
+
then:
|
|
209
|
+
properties:
|
|
210
|
+
query:
|
|
211
|
+
not:
|
|
212
|
+
const: false
|
|
161
213
|
- if: # not transition
|
|
162
214
|
not:
|
|
163
215
|
properties:
|
|
@@ -167,6 +219,30 @@ properties:
|
|
|
167
219
|
properties:
|
|
168
220
|
concurrency:
|
|
169
221
|
const: ~ # never pass
|
|
222
|
+
- if: # neither a transition nor an assignment
|
|
223
|
+
not:
|
|
224
|
+
properties:
|
|
225
|
+
type:
|
|
226
|
+
enum: [transition, assignment]
|
|
227
|
+
then:
|
|
228
|
+
properties:
|
|
229
|
+
# An operation that writes nothing has nothing to record. An effect writes, but
|
|
230
|
+
# its write is get-or-create, so a second arrival of one writes nothing already —
|
|
231
|
+
# and it writes in `acquire`, before its algorithm has run, so its reply is not
|
|
232
|
+
# known where the record is made.
|
|
233
|
+
once:
|
|
234
|
+
const: ~ # never pass
|
|
235
|
+
- if: # commits more than one entity
|
|
236
|
+
properties:
|
|
237
|
+
type:
|
|
238
|
+
const: transition
|
|
239
|
+
scope:
|
|
240
|
+
const: objects
|
|
241
|
+
then:
|
|
242
|
+
properties:
|
|
243
|
+
# the reply is recorded, and over a set it is unbounded
|
|
244
|
+
once:
|
|
245
|
+
const: ~ # never pass
|
|
170
246
|
- if: # observation
|
|
171
247
|
properties:
|
|
172
248
|
type:
|
|
@@ -174,7 +250,7 @@ properties:
|
|
|
174
250
|
then:
|
|
175
251
|
properties:
|
|
176
252
|
scope:
|
|
177
|
-
enum: [object, objects, none]
|
|
253
|
+
enum: [object, objects, stream, none]
|
|
178
254
|
query:
|
|
179
255
|
not:
|
|
180
256
|
const: false
|
|
@@ -189,15 +265,13 @@ properties:
|
|
|
189
265
|
- if: # computation
|
|
190
266
|
properties:
|
|
191
267
|
type:
|
|
192
|
-
|
|
268
|
+
enum: [computation, unmanaged]
|
|
193
269
|
then:
|
|
194
270
|
properties:
|
|
195
271
|
scope:
|
|
196
272
|
const: none
|
|
197
|
-
default: none
|
|
198
273
|
query:
|
|
199
274
|
const: false
|
|
200
|
-
default: false
|
|
201
275
|
- if: # effect
|
|
202
276
|
properties:
|
|
203
277
|
type:
|
|
@@ -205,20 +279,16 @@ properties:
|
|
|
205
279
|
then:
|
|
206
280
|
properties:
|
|
207
281
|
scope:
|
|
208
|
-
|
|
209
|
-
default: none
|
|
210
|
-
query:
|
|
211
|
-
const: false
|
|
212
|
-
default: false
|
|
282
|
+
enum: [object, objects, stream, none]
|
|
213
283
|
additionalProperties: false
|
|
214
284
|
additionalProperties: false
|
|
215
285
|
|
|
216
286
|
events:
|
|
217
287
|
type: object
|
|
218
288
|
propertyNames:
|
|
219
|
-
$ref:
|
|
289
|
+
$ref: "#/definitions/name"
|
|
220
290
|
patternProperties:
|
|
221
|
-
|
|
291
|
+
".*":
|
|
222
292
|
type: object
|
|
223
293
|
properties:
|
|
224
294
|
bridge:
|
|
@@ -226,49 +296,53 @@ properties:
|
|
|
226
296
|
path:
|
|
227
297
|
type: string
|
|
228
298
|
binding:
|
|
229
|
-
$ref:
|
|
299
|
+
$ref: "#/definitions/binding"
|
|
230
300
|
conditioned:
|
|
231
301
|
type: boolean
|
|
232
|
-
default: false
|
|
233
302
|
subjective:
|
|
234
303
|
type: boolean
|
|
235
|
-
default: false
|
|
236
304
|
required: [bridge, path]
|
|
237
305
|
additionalProperties: false
|
|
238
306
|
|
|
239
307
|
receivers:
|
|
240
308
|
type: object
|
|
241
309
|
patternProperties:
|
|
242
|
-
|
|
310
|
+
".*":
|
|
243
311
|
type: object
|
|
244
312
|
properties:
|
|
245
313
|
operation:
|
|
246
|
-
$ref:
|
|
314
|
+
$ref: "#/definitions/name"
|
|
247
315
|
bridge:
|
|
248
316
|
type: string
|
|
249
317
|
binding:
|
|
250
318
|
type: string
|
|
251
319
|
source:
|
|
252
|
-
$ref:
|
|
320
|
+
$ref: "#/definitions/name"
|
|
253
321
|
not:
|
|
254
322
|
const: context
|
|
255
323
|
path:
|
|
256
324
|
type: string
|
|
257
325
|
conditioned:
|
|
258
326
|
type: boolean
|
|
259
|
-
default: false
|
|
260
327
|
adaptive:
|
|
261
328
|
type: boolean
|
|
262
|
-
default: false
|
|
263
329
|
arguments:
|
|
264
330
|
type: array
|
|
265
331
|
required: [operation]
|
|
266
332
|
additionalProperties: false
|
|
267
333
|
|
|
268
|
-
|
|
334
|
+
guards:
|
|
269
335
|
type: object
|
|
336
|
+
patternProperties:
|
|
337
|
+
".*":
|
|
338
|
+
type: object
|
|
339
|
+
properties:
|
|
340
|
+
bridge: { type: string }
|
|
341
|
+
path: { type: string }
|
|
342
|
+
required: [bridge, path]
|
|
343
|
+
additionalProperties: false
|
|
270
344
|
|
|
271
|
-
|
|
345
|
+
extensions:
|
|
272
346
|
type: object
|
|
273
347
|
|
|
274
348
|
additionalProperties: false
|
|
@@ -1,25 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
import path from 'node:path'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
import { yaml } from '@toa.io/generic'
|
|
5
|
+
import * as schemas from '@toa.io/schemas'
|
|
6
|
+
import { definition } from '../definition.js'
|
|
4
7
|
|
|
5
|
-
const
|
|
6
|
-
|
|
8
|
+
const object = yaml.load(
|
|
9
|
+
readFileSync(path.resolve(import.meta.dirname, 'schema.yaml'), 'utf8')
|
|
10
|
+
)
|
|
11
|
+
const schema = schemas.schema(object)
|
|
7
12
|
|
|
8
|
-
const
|
|
9
|
-
const schema = new Schema(object)
|
|
10
|
-
|
|
11
|
-
const validate = (manifest) => {
|
|
13
|
+
export const validate = async (manifest) => {
|
|
12
14
|
const error = schema.fit(manifest)
|
|
13
15
|
|
|
14
|
-
if (error) throw
|
|
16
|
+
if (error) throw error
|
|
15
17
|
|
|
16
|
-
if (manifest.
|
|
18
|
+
if (manifest.entity !== undefined) entity(manifest)
|
|
19
|
+
if (manifest.events !== undefined) await events(manifest)
|
|
17
20
|
if (manifest.receivers !== undefined) receivers(manifest)
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
/** What only the entity's own declaration can answer: whether a name it uses is one it declares. */
|
|
24
|
+
const entity = (manifest) => {
|
|
25
|
+
const { properties, required, blank } = manifest.entity
|
|
26
|
+
|
|
27
|
+
for (const name of required ?? [])
|
|
28
|
+
if (properties[name] === undefined)
|
|
29
|
+
throw new Error(`Entity requires property '${name}', which is not defined`)
|
|
30
|
+
|
|
31
|
+
for (const name of Object.keys(blank ?? {}))
|
|
32
|
+
if (properties[name] === undefined)
|
|
33
|
+
throw new Error(`Entity blank names property '${name}', which is not defined`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const events = async (manifest) => {
|
|
21
37
|
for (const [label, event] of Object.entries(manifest.events)) {
|
|
22
|
-
|
|
38
|
+
const { properties } = (await definition(event.binding)).module
|
|
39
|
+
|
|
40
|
+
if (properties.async !== true) {
|
|
23
41
|
throw new Error(`Event '${label}' binding '${event.binding}' is not async`)
|
|
24
42
|
}
|
|
25
43
|
}
|
|
@@ -28,15 +46,17 @@ const events = (manifest) => {
|
|
|
28
46
|
const receivers = (manifest) => {
|
|
29
47
|
for (const [locator, receiver] of Object.entries(manifest.receivers)) {
|
|
30
48
|
if (manifest.operations?.[receiver.operation] === undefined) {
|
|
31
|
-
throw new Error(
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Receiver '${locator}' refers to undefined operation '${receiver.operation}'`
|
|
51
|
+
)
|
|
32
52
|
}
|
|
33
53
|
|
|
34
54
|
if (!TYPES.has(manifest.operations[receiver.operation].type)) {
|
|
35
|
-
throw new Error(
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Receiver '${locator}' must refer to an operation of the allowed types: ${Array.from(TYPES).join(', ')}`
|
|
57
|
+
)
|
|
36
58
|
}
|
|
37
59
|
}
|
|
38
60
|
}
|
|
39
61
|
|
|
40
|
-
const TYPES = new Set(['transition', 'effect'])
|
|
41
|
-
|
|
42
|
-
exports.validate = validate
|
|
62
|
+
const TYPES = new Set(['transition', 'assignment', 'effect', 'unmanaged'])
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const connectors = (context, extracted) => {
|
|
3
|
+
export const connectors = (context, extracted) => {
|
|
6
4
|
const connectors = {}
|
|
7
5
|
|
|
8
|
-
const components =
|
|
6
|
+
const components =
|
|
7
|
+
(context.components === undefined
|
|
9
8
|
? extracted
|
|
10
|
-
: context.components.concat(extracted)
|
|
11
|
-
) ?? []
|
|
9
|
+
: context.components.concat(extracted)) ?? []
|
|
12
10
|
|
|
13
11
|
for (const component of components) {
|
|
14
12
|
if (component.entity !== undefined) {
|
|
@@ -47,5 +45,3 @@ const connectors = (context, extracted) => {
|
|
|
47
45
|
|
|
48
46
|
return connectors
|
|
49
47
|
}
|
|
50
|
-
|
|
51
|
-
exports.connectors = connectors
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* @param {toa.norm.Component} component
|
|
5
3
|
* @param {Object} manifest
|
|
6
4
|
* @returns {toa.norm.context.dependencies.Instance}
|
|
7
5
|
*/
|
|
8
|
-
const describe = (component, manifest = undefined) => ({
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
export const describe = (component, manifest = undefined) => ({
|
|
7
|
+
locator: component.locator,
|
|
8
|
+
manifest
|
|
9
|
+
})
|
|
@@ -1,12 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
import { Locator } from '@toa.io/core'
|
|
2
|
+
import { converge } from '../converge.js'
|
|
3
|
+
import { component as load } from '../../component.js'
|
|
4
|
+
import { definition } from '../../definition.js'
|
|
2
5
|
|
|
3
|
-
const extensions = async (context) => {
|
|
6
|
+
export const extensions = async (context) => {
|
|
4
7
|
const extensions = {}
|
|
5
8
|
const components = context.components?.slice() ?? []
|
|
6
|
-
|
|
9
|
+
|
|
10
|
+
/** the components each extension brings, by reference
|
|
11
|
+
* @type {Map<string, toa.norm.Component[]>} */
|
|
12
|
+
const contributed = new Map()
|
|
13
|
+
|
|
14
|
+
// an extension a composition runs may be referenced by no component of this context,
|
|
15
|
+
// and its own components must be extracted all the same
|
|
16
|
+
const declared = await extractDeclaredServices(context, extensions, contributed)
|
|
17
|
+
|
|
18
|
+
const extracted = declared.concat(
|
|
19
|
+
await extractExtensionComponents(
|
|
20
|
+
components.concat(declared),
|
|
21
|
+
extensions,
|
|
22
|
+
contributed,
|
|
23
|
+
context.annotations
|
|
24
|
+
)
|
|
25
|
+
)
|
|
7
26
|
|
|
8
27
|
components.push(...extracted)
|
|
9
28
|
|
|
29
|
+
// after the extraction, because what an extension ships converges like anything else
|
|
30
|
+
converge(context, components)
|
|
31
|
+
|
|
10
32
|
for (const component of components) {
|
|
11
33
|
if (component.extensions === undefined) continue
|
|
12
34
|
|
|
@@ -17,12 +39,33 @@ const extensions = async (context) => {
|
|
|
17
39
|
}
|
|
18
40
|
}
|
|
19
41
|
|
|
20
|
-
return { extensions, components: extracted }
|
|
42
|
+
return { extensions, components: extracted, contributed }
|
|
21
43
|
}
|
|
22
44
|
|
|
23
|
-
async function
|
|
24
|
-
const
|
|
45
|
+
async function extractDeclaredServices(context, extensions, contributed) {
|
|
46
|
+
const extracted = []
|
|
47
|
+
|
|
48
|
+
for (const composition of context.compositions ?? [])
|
|
49
|
+
for (const reference of composition.services ?? []) {
|
|
50
|
+
if (reference in extensions) continue
|
|
25
51
|
|
|
52
|
+
try {
|
|
53
|
+
extracted.push(
|
|
54
|
+
...(await extract(reference, extensions, contributed, context.annotations))
|
|
55
|
+
)
|
|
56
|
+
} catch (e) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Composition '${composition.name}' lists '${reference}', ` +
|
|
59
|
+
`which cannot be resolved: ${e.message}`,
|
|
60
|
+
{ cause: e }
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return extracted
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function extractExtensionComponents(components, extensions, contributed, annotations) {
|
|
26
69
|
const extracted = []
|
|
27
70
|
|
|
28
71
|
for (const component of components) {
|
|
@@ -31,26 +74,56 @@ async function extractExtensionComponents (components, extensions) {
|
|
|
31
74
|
for (const reference of Object.keys(component.extensions)) {
|
|
32
75
|
if (reference in extensions) continue
|
|
33
76
|
|
|
34
|
-
|
|
77
|
+
extracted.push(...(await extract(reference, extensions, contributed, annotations)))
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (extracted.length === 0) return extracted
|
|
35
82
|
|
|
36
|
-
|
|
83
|
+
const deeper = await extractExtensionComponents(
|
|
84
|
+
extracted,
|
|
85
|
+
extensions,
|
|
86
|
+
contributed,
|
|
87
|
+
annotations
|
|
88
|
+
)
|
|
37
89
|
|
|
38
|
-
|
|
90
|
+
return extracted.concat(deeper)
|
|
91
|
+
}
|
|
39
92
|
|
|
40
|
-
|
|
41
|
-
|
|
93
|
+
/**
|
|
94
|
+
* The components an extension contributes, if it contributes any.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} reference
|
|
97
|
+
* @param {object} extensions
|
|
98
|
+
* @param {Map<string, toa.norm.Component[]>} contributed
|
|
99
|
+
* @param {object} [annotations]
|
|
100
|
+
* @returns {Promise<Array<toa.norm.Component>>}
|
|
101
|
+
*/
|
|
102
|
+
async function extract(reference, extensions, contributed, annotations) {
|
|
103
|
+
extensions[reference] = []
|
|
42
104
|
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
105
|
+
const { name, module: mod } = await definition(reference)
|
|
47
106
|
|
|
48
|
-
if (
|
|
49
|
-
return extracted
|
|
107
|
+
if (mod.components === undefined) return []
|
|
50
108
|
|
|
51
|
-
|
|
109
|
+
// the annotation decides whether an extension contributes components at all
|
|
110
|
+
const annotation = annotations?.[name]
|
|
111
|
+
const { manifests, paths } = mod.components(annotation)
|
|
52
112
|
|
|
53
|
-
|
|
113
|
+
// a definition carries the manifests, read where the extension is not installed;
|
|
114
|
+
// an entry names the directories, where it is
|
|
115
|
+
const extracted = manifests === undefined ? [] : manifests.map(revive)
|
|
116
|
+
|
|
117
|
+
if (manifests === undefined) for (const path of paths) extracted.push(await load(path))
|
|
118
|
+
|
|
119
|
+
contributed.set(reference, extracted)
|
|
120
|
+
|
|
121
|
+
return extracted
|
|
54
122
|
}
|
|
55
123
|
|
|
56
|
-
|
|
124
|
+
/** A manifest as a digest carries it, which is one norm read, without the locator. */
|
|
125
|
+
function revive(manifest) {
|
|
126
|
+
manifest.locator = new Locator(manifest.name, manifest.namespace)
|
|
127
|
+
|
|
128
|
+
return manifest
|
|
129
|
+
}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const { extensions } = require('./extensions')
|
|
5
|
-
const { resolve } = require('./resolve')
|
|
6
|
-
|
|
7
|
-
exports.connectors = connectors
|
|
8
|
-
exports.extensions = extensions
|
|
9
|
-
exports.resolve = resolve
|
|
1
|
+
export { connectors } from './connectors.js'
|
|
2
|
+
export { extensions } from './extensions.js'
|
|
3
|
+
export { resolve } from './resolve.js'
|