@reventlessdev/reventless-core 3.0.0-alpha.260 → 3.0.0-alpha.262
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 +24 -0
- package/package.json +10 -10
- package/src/Behavior.res +1 -4
- package/src/Message.res +17 -14
- package/src/Projection.res +51 -25
- package/src/RequestContext.res +8 -9
- package/src/ResourceAttribution.res +28 -14
- package/src/adapter/Adapter.res +48 -4
- package/src/adapter/EventLogProvisioning/EventLogProvisioning.res +5 -5
- package/src/adapter/Messaging/Messaging_Log_Backend.res +10 -7
- package/src/adapter/Messaging/Messaging_Log_Backend.res.mjs +2 -4
- package/src/adapter/Monitoring/Monitoring.res +16 -17
- package/src/adapter/QueryInterception/QueryInterception.res +0 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +12 -5
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +11 -7
- package/src/adapter/Runtime/PluginRuntime_Builder_Micro.res +1 -7
- package/src/adapter/Runtime/PluginRuntime_Builder_Single.res +1 -7
- package/src/adapter/RuntimeExtension/RuntimeExtension.res +0 -1
- package/src/adapter/Secrets/Secrets_Node_Backend.res +60 -0
- package/src/adapter/Secrets/Secrets_Node_Backend.res.mjs +69 -0
- package/src/admin/Platform_Admin.res +17 -6
- package/src/admin/Platform_AdminApi.res +14 -9
- package/src/admin/Platform_Admin_Callback.res +1 -1
- package/src/admin/Platform_BakedManifest.res +34 -34
- package/src/admin/Platform_ComponentDefinitionsApi.res +9 -21
- package/src/admin/Platform_PluginStructuresApi.res +15 -12
- package/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry.res +1 -1
- package/src/components/Aggregate/Aggregate.res +13 -12
- package/src/components/Aggregate/Aggregate_Builder.res +22 -21
- package/src/components/Aggregate/Aggregate_Callback.res +91 -86
- package/src/components/Api/ApiNoApiHelpers.res +1 -3
- package/src/components/Api/Api_Ids.res +0 -1
- package/src/components/Api/Api_Naming.res +41 -18
- package/src/components/Api/GraphQL_FragmentGenerator.res +38 -51
- package/src/components/Api/GraphQL_SchemaInspector.res +5 -3
- package/src/components/Api/GraphQL_Stitcher.res +9 -10
- package/src/components/Api/MCP_SchemaGenerator.res +8 -12
- package/src/components/Api/OwnerScopeDiagnostics.res +1 -2
- package/src/components/Api/QueryDbListQuery.res +13 -6
- package/src/components/Api/SchemaType.res +28 -23
- package/src/components/Api/SuryToJsonSchema.res +9 -10
- package/src/components/AutomationSlice/AutomationSlice.res +4 -4
- package/src/components/AutomationSlice/AutomationSlice_Builder.res +15 -14
- package/src/components/AutomationSlice/AutomationSlice_Callback.res +3 -3
- package/src/components/Builder_Helpers.res +12 -23
- package/src/components/CommandGenerator/CommandGenerator.res +6 -1
- package/src/components/CommandGenerator/CommandGenerator_Builder.res +29 -21
- package/src/components/CommandGenerator/CommandGenerator_Callback.res +17 -21
- package/src/components/CommandTopic/CommandTopic.res +3 -3
- package/src/components/CommandTopic/CommandTopic_Builder.res +10 -10
- package/src/components/CommandTopic/CommandTopic_Callback.res +1 -4
- package/src/components/CommandTopic/CommandTopic_Helpers.res +1 -2
- package/src/components/Counter/Counter_Builder.res +1 -6
- package/src/components/Counter/Counter_Callback.res +3 -3
- package/src/components/Dcb/Dcb_Builder.res +970 -956
- package/src/components/DcbEventLog/DcbEventLog.res +4 -4
- package/src/components/DcbEventLog/DcbEventLog_Builder.res +0 -1
- package/src/components/DcbEventLog/DcbEventLog_Operations.res +17 -18
- package/src/components/EventCollector/EventCollector_Builder.res +2 -1
- package/src/components/EventLog/EventLog.res +5 -1
- package/src/components/EventLog/EventLog_Operations.res +12 -8
- package/src/components/EventMapper/EventMapper.res +8 -10
- package/src/components/EventMapper/EventMapper_Builder.res +4 -1
- package/src/components/EventMapper/EventMapper_Callback.res +46 -31
- package/src/components/EventTopic/EventTopic.res +3 -3
- package/src/components/EventTopic/EventTopic_Builder.res +1 -1
- package/src/components/Extension/Extension.res +4 -1
- package/src/components/Extension/Extension_Operations.res +11 -3
- package/src/components/ExtensionPoint/ExtensionPoint.res +3 -3
- package/src/components/ExtensionPoint/ExtensionPoint_Builder.res +9 -10
- package/src/components/ExtensionPoint/ExtensionPoint_Callback.res +2 -6
- package/src/components/ExtensionPoint/ExtensionPoint_Operations.res +13 -5
- package/src/components/Heartbeat/Heartbeat_Callback.res +3 -3
- package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +4 -4
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +7 -10
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice.res +4 -4
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +15 -14
- package/src/components/QueryDb/QueryDb_Adapter.res +1 -3
- package/src/components/QueryDb/QueryDb_Operations.res +10 -10
- package/src/components/ReadModel/ReadModel.res +3 -3
- package/src/components/ReadModel/ReadModel_Builder.res +1 -2
- package/src/components/ReadModel/ReadModel_Callback.res +2 -1
- package/src/components/SideEffectHandler/SideEffectHandler_Callback.res +5 -2
- package/src/components/StateChangeSlice/StateChangeSlice.res +3 -3
- package/src/components/StateChangeSlice/StateChangeSlice_Builder.res +23 -4
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +22 -6
- package/src/components/StateTopic.res +1 -3
- package/src/components/StateViewSlice/StateViewSlice.res +4 -4
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +79 -68
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +3 -1
- package/src/components/Task/Task.res +3 -3
- package/src/components/Task/Task_Adapter.res +4 -1
- package/src/components/Task/Task_Builder.res +24 -21
- package/src/plugin/api/PluginBaseFragment.res +9 -3
- package/src/plugin/component/Capability_Inference.res +8 -10
- package/src/plugin/component/Plugin.res +6 -6
- package/src/plugin/component/Plugin_Builder.res +54 -50
- package/src/plugin/component/Plugin_Callback.res +10 -6
- package/src/plugin/component/Plugin_Helpers.res +264 -230
- package/src/plugin/component/Plugin_Structure.res +370 -324
- package/src/plugin/component/Plugin_SubscriptionSchema.res +8 -6
- package/src/plugin/component/SchemaWalker.res +11 -12
- package/src/plugin/connect/PluginConnectExtension_Mapping.res +45 -50
- package/src/plugin/lifecycle/PluginBehavior.res +3 -4
- package/src/plugin/lifecycle/PluginSpec.res +1 -2
- package/src/plugin/lifecycle/PluginsProjection.res +16 -5
- package/src/plugin/lifecycle/PluginsReadModelSpec.res +0 -2
- package/src/util/CommandPublisher.res +9 -2
- package/src/util/CsvStream.res +3 -3
- package/src/util/Interstack.res +7 -15
- package/src/util/Interstack.resi +3 -3
- package/src/util/LogFormat.res +5 -9
- package/src/util/Logger.res +1 -2
- package/src/util/Lru.res +2 -1
- package/src/util/Lru.resi +2 -3
- package/src/util/ScheduleOps.res +24 -27
- package/src/util/StorePrefixCollision.res +6 -9
- package/src/util/Util_Promise.res +4 -1
- package/src/util/Util_Promise.res.mjs +1 -1
- package/tests/IdentityTest.res +166 -113
- package/tests/ResourceAttributionTest.res +28 -25
- package/tests/Secrets_Node_BackendTest.res +141 -0
- package/tests/Secrets_Node_BackendTest.res.mjs +152 -0
- package/tests/StorePrefixCollisionTest.res +2 -1
- package/tests/TaskBucketNamingTest.res +3 -2
- package/tests/adapter/EventLogProvisioningTest.res +51 -48
- package/tests/adapter/MonitoringTest.res +81 -38
- package/tests/adapter/QueryInterceptionTest.res +2 -2
- package/tests/adapter/RefusalVocabularyTest.res +31 -23
- package/tests/adapter/RuntimeExtensionHookReachTest.res +9 -8
- package/tests/adapter/RuntimeExtensionTest.res +5 -3
- package/tests/admin/AdminApiSchemaDriftTest.res +22 -16
- package/tests/admin/Platform_Admin_StructureTest.res +22 -14
- package/tests/admin/Platform_Admin_StructureTest.res.mjs +4 -3
- package/tests/admin/Platform_BakedManifestTest.res +31 -35
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +18 -15
- package/tests/admin/Platform_PluginStructuresApiTest.res +5 -3
- package/tests/admin/Platform_UIFragmentsApiTest.res +6 -3
- package/tests/aggregate/AggregateCacheTest.res +10 -12
- package/tests/aggregate/AggregateCallbackTest.res +75 -46
- package/tests/aggregate/AggregateCausationTest.res +19 -20
- package/tests/aggregate/AggregateConflictTest.res +41 -19
- package/tests/aggregate/AggregateFixtures.res +15 -5
- package/tests/aggregate/AggregateRejectionTest.res +97 -80
- package/tests/aggregate/AggregateSnapshotTest.res +44 -16
- package/tests/api/Api_IdsTest.res +10 -4
- package/tests/api/BrandedMarkerCompositionTest.res +48 -36
- package/tests/api/GeolocationSemanticTest.res +3 -3
- package/tests/api/GraphQL_FragmentGeneratorTest.res +66 -54
- package/tests/api/OwnerScopeDiagnosticsTest.res +31 -27
- package/tests/api/StorageRefFieldsTest.res +30 -18
- package/tests/api/SuryToJsonSchemaTest.res +1315 -1014
- package/tests/api/TaggedUnionSchemaTest.res +17 -24
- package/tests/commandgenerator/CommandGeneratorCallbackTest.res +235 -179
- package/tests/commandgenerator/CommandGeneratorFixtures.res +8 -13
- package/tests/commandgenerator/OwnerStampingTest.res +91 -76
- package/tests/commandtopic/CommandOutcomeHookTest.res +198 -155
- package/tests/commandtopic/CommandTopicCallbackFixtures.res +5 -4
- package/tests/commandtopic/CommandTopicCallbackTest.res +61 -43
- package/tests/commandtopic/CommandTopicHelpersRejectionTest.res +70 -55
- package/tests/counter/CounterCallbackTest.res +40 -34
- package/tests/counter/CounterFixtures.res +4 -4
- package/tests/dcb/DcbDecodeTest.res +115 -84
- package/tests/dcb/DcbEventLogOperationsTest.res +194 -143
- package/tests/dcb/DcbEventLogSchemaTest.res +13 -13
- package/tests/dcb/DcbFixtures.res +4 -5
- package/tests/dcb/DcbScopeInferenceTest.res +173 -114
- package/tests/dcb/DcbStateChangeSliceTest.res +391 -262
- package/tests/dcb/DcbTagTest.res +22 -11
- package/tests/dcb/DcbValidationTest.res +288 -218
- package/tests/eventlog/EventLogFixtures.res +4 -4
- package/tests/eventlog/EventLogOperationsTest.res +102 -67
- package/tests/eventlog/EventLogRetryTest.res +104 -65
- package/tests/eventmapper/EventMapperCallbackTest.res +118 -83
- package/tests/eventmapper/EventMapperFixtures.res +15 -23
- package/tests/eventmapper/EventMapperTest.res +54 -38
- package/tests/eventtopic/EventTopicFixtures.res +3 -4
- package/tests/eventtopic/EventTopicOperationsTest.res +41 -29
- package/tests/extensionpoint/ExtensionPointCallbackTest.res +58 -45
- package/tests/extensionpoint/ExtensionPointFixtures.res +16 -20
- package/tests/extensionpoint/ExtensionPointOperationsTest.res +70 -49
- package/tests/extensionpoint/PluginDisconnectScheduleTest.res +2 -1
- package/tests/logger/LogFormatTest.res +104 -82
- package/tests/logger/LoggerLazyTest.res +21 -12
- package/tests/message/MessageTest.res +96 -93
- package/tests/message/MetaEnvelopeTest.res +26 -30
- package/tests/plugin/HeartbeatDisconnectGraceTest.res +31 -25
- package/tests/plugin/ManifestVisibilityTest.res +20 -12
- package/tests/plugin/PluginDefinitionScalars.res +1 -2
- package/tests/plugin/PluginLifecycleCorpusTest.res +15 -12
- package/tests/plugin/PluginSpecExperiment.res +0 -1
- package/tests/plugin/PluginStructureTest.res +1529 -1045
- package/tests/plugin/Plugin_EventQuerySchemaTest.res +21 -10
- package/tests/plugin/SchemaWalkerTest.res +116 -70
- package/tests/plugin/StateChange/PsReserveStock.res +4 -4
- package/tests/plugin/StateView/PsAnnotatedView.res +4 -4
- package/tests/plugin/StateView/PsCustomersView.res +4 -4
- package/tests/plugin/StateView/PsShipmentsView.res +3 -2
- package/tests/projection/LifecycleTrailTest.res +1 -4
- package/tests/projection/ProjectionOptimizeTest.res +13 -9
- package/tests/querydb/QueryDbFixtures.res +0 -1
- package/tests/querydb/QueryDbOperationsTest.res +99 -64
- package/tests/querydb/TaggedUnionStampTest.res +10 -8
- package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res +50 -35
- package/tests/sideeffecthandler/SideEffectHandlerFixtures.res +4 -10
- package/tests/util/CommandPublisherTest.res +0 -1
- package/tests/util/CsvStreamTest.res +3 -3
- package/tests/util/LruTest.res +84 -56
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
let log = Logger.fromEnv()
|
|
6
6
|
|
|
7
7
|
// Set by `check:lifecycle`, and by nothing else. See the note where it is read.
|
|
8
|
-
@val
|
|
8
|
+
@val
|
|
9
|
+
external _declaredTransitionsOnly: option<string> =
|
|
10
|
+
"process.env.REVENTLESS_DECLARED_TRANSITIONS_ONLY"
|
|
9
11
|
let declaredTransitionsOnly = _declaredTransitionsOnly->Option.isSome
|
|
10
12
|
|
|
11
13
|
// Whether a field can name a record, stated over `SchemaType`'s IR so every
|
|
@@ -26,8 +28,11 @@ let rec isLabelShape = (t: SchemaType.schemaType): bool =>
|
|
|
26
28
|
// and *exactly*: `customerName` holds a customer's name, not this record's.
|
|
27
29
|
let conventionalLabelNames = ["name", "title", "label", "displayname"]
|
|
28
30
|
|
|
29
|
-
let shapeOfField = (
|
|
30
|
-
|
|
31
|
+
let shapeOfField = (
|
|
32
|
+
~entityName: string,
|
|
33
|
+
~name: string,
|
|
34
|
+
schema: S.t<unknown>,
|
|
35
|
+
): SchemaType.schemaType => SchemaType.fromSury(~parentName=entityName, ~fieldName=name, schema)
|
|
31
36
|
|
|
32
37
|
// The field holding the entity's lifecycle, for filtering a per-row command
|
|
33
38
|
// menu. One rule, stated in `Reventless.Lifecycle` — the projection machinery
|
|
@@ -36,9 +41,9 @@ let lifecycleFieldFromStateSchema = Reventless.Lifecycle.fieldName
|
|
|
36
41
|
|
|
37
42
|
// The field whose truth withdraws a row from ordinary reads. Annotation-only —
|
|
38
43
|
// no convention rung, since guessing wrong here makes rows vanish.
|
|
39
|
-
let retiredFromStateSchema = (
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
let retiredFromStateSchema = (stateSchema: S.t<unknown>): option<
|
|
45
|
+
Reventless.StateAnnotations.retiredSpec,
|
|
46
|
+
> =>
|
|
42
47
|
switch Reventless.StateAnnotations.getSpec(stateSchema) {
|
|
43
48
|
| Some(spec) => spec.retired
|
|
44
49
|
| None => None
|
|
@@ -112,11 +117,10 @@ let checkRetiredValue = (~entityName: string, stateSchema: S.t<unknown>): retire
|
|
|
112
117
|
Checked(
|
|
113
118
|
values
|
|
114
119
|
->Array.filter(v => !(declared->Array.includes(v)))
|
|
115
|
-
->Array.map(
|
|
116
|
-
v
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)}.`,
|
|
120
|
+
->Array.map(v =>
|
|
121
|
+
`${entityName}: @retired(${v}) names a state "${field}" does not declare — known values: ${declared->Array.join(
|
|
122
|
+
", ",
|
|
123
|
+
)}.`
|
|
120
124
|
),
|
|
121
125
|
)
|
|
122
126
|
}
|
|
@@ -149,10 +153,9 @@ let reportRetiredStates = (
|
|
|
149
153
|
// The states a record's lifecycle field can hold — the field a command's
|
|
150
154
|
// declared edge is written in terms of. `None`: no lifecycle; `Some([])`: one
|
|
151
155
|
// with no cases.
|
|
152
|
-
let lifecycleStatesFromStateSchema = (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
): option<array<string>> =>
|
|
156
|
+
let lifecycleStatesFromStateSchema = (~entityName: string, stateSchema: S.t<unknown>): option<
|
|
157
|
+
array<string>,
|
|
158
|
+
> =>
|
|
156
159
|
lifecycleFieldFromStateSchema(stateSchema)->Option.flatMap(field =>
|
|
157
160
|
switch stateSchema {
|
|
158
161
|
| Object({properties}) =>
|
|
@@ -214,9 +217,10 @@ let translationTableDrift = (
|
|
|
214
217
|
~observed: array<(string, string)>,
|
|
215
218
|
~followed: array<string>,
|
|
216
219
|
): (array<string>, array<string>) => {
|
|
217
|
-
let declaredEdges =
|
|
218
|
-
Array.
|
|
219
|
-
|
|
220
|
+
let declaredEdges =
|
|
221
|
+
declared->Array.reduce([], (acc, {name, fromEventTypes}) =>
|
|
222
|
+
Array.concat(acc, fromEventTypes->Array.map(src => (src, name)))
|
|
223
|
+
)
|
|
220
224
|
let has = (edges, (src, pub)) => edges->Array.some(((s, p)) => s == src && p == pub)
|
|
221
225
|
|
|
222
226
|
let failures =
|
|
@@ -370,13 +374,14 @@ let checkDeclaredTransitions = (
|
|
|
370
374
|
},
|
|
371
375
|
)
|
|
372
376
|
if Array.length(declared) > 0 {
|
|
373
|
-
let known =
|
|
374
|
-
|
|
377
|
+
let known = w.linkedViews->Array.reduce(
|
|
378
|
+
[],
|
|
379
|
+
(acc, view) =>
|
|
375
380
|
switch lifecycleStatesByView->Dict.get(view) {
|
|
376
381
|
| Some(states) => Array.concat(acc, states)
|
|
377
382
|
| None => acc
|
|
378
|
-
}
|
|
379
|
-
|
|
383
|
+
},
|
|
384
|
+
)
|
|
380
385
|
if Array.length(known) == 0 {
|
|
381
386
|
unvalidated := unvalidated.contents + 1
|
|
382
387
|
} else {
|
|
@@ -385,21 +390,23 @@ let checkDeclaredTransitions = (
|
|
|
385
390
|
// longer declares means the committed model went stale, and stopping is
|
|
386
391
|
// right — published, it would offer the command on no row at all.
|
|
387
392
|
let hint = switch cmd.allowedStatesSource {
|
|
388
|
-
| Some(
|
|
389
|
-
|
|
393
|
+
| Some(
|
|
394
|
+
"derived",
|
|
395
|
+
) => " This edge came from the component's own scenarios, so the committed lifecycle model is stale."
|
|
390
396
|
| _ => ""
|
|
391
397
|
}
|
|
392
398
|
declared
|
|
393
399
|
->Array.filter(state => !(known->Array.includes(state)))
|
|
394
|
-
->Array.forEach(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
400
|
+
->Array.forEach(
|
|
401
|
+
state =>
|
|
402
|
+
failures
|
|
403
|
+
->Array.push(
|
|
404
|
+
`${w.name}.${cmd.name} declares state "${state}", which none of its ` ++
|
|
405
|
+
`linked views declare — ${w.linkedViews->Array.join(
|
|
406
|
+
", ",
|
|
407
|
+
)} know ${known->Array.join(", ")}.${hint}`,
|
|
408
|
+
)
|
|
409
|
+
->ignore,
|
|
403
410
|
)
|
|
404
411
|
}
|
|
405
412
|
}
|
|
@@ -412,15 +419,13 @@ let checkDeclaredTransitions = (
|
|
|
412
419
|
if unvalidated.contents > 0 {
|
|
413
420
|
log.warn(
|
|
414
421
|
~comp="Plugin_Structure",
|
|
415
|
-
`${pluginName}: ${unvalidated.contents->Int.toString} command(s) declare a transition ` ++
|
|
416
|
-
`but no linked view declares a lifecycle to check it against.`,
|
|
422
|
+
`${pluginName}: ${unvalidated.contents->Int.toString} command(s) declare a transition ` ++ `but no linked view declares a lifecycle to check it against.`,
|
|
417
423
|
)
|
|
418
424
|
}
|
|
419
425
|
|
|
420
426
|
if Array.length(failures) > 0 {
|
|
421
427
|
JsError.throwWithMessage(
|
|
422
|
-
`${pluginName}: a transition names states that do not exist.\n` ++
|
|
423
|
-
failures->Array.join("\n"),
|
|
428
|
+
`${pluginName}: a transition names states that do not exist.\n` ++ failures->Array.join("\n"),
|
|
424
429
|
)
|
|
425
430
|
}
|
|
426
431
|
}
|
|
@@ -439,11 +444,12 @@ let lifecycleTopologyFindings = (
|
|
|
439
444
|
->Array.forEach(((view, states)) => {
|
|
440
445
|
// Arrival only, so a creating command (a target and no from-set) counts
|
|
441
446
|
// towards reachability like any other edge.
|
|
442
|
-
let reachable =
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
+
let reachable =
|
|
448
|
+
writables->Array.reduce([], (acc, w) =>
|
|
449
|
+
w.linkedViews->Array.includes(view)
|
|
450
|
+
? Array.concat(acc, w.commands->Array.filterMap(cmd => cmd.targetState))
|
|
451
|
+
: acc
|
|
452
|
+
)
|
|
447
453
|
if Array.length(reachable) > 0 {
|
|
448
454
|
// Rows start in the first declared state, so nothing pointing at it is fine.
|
|
449
455
|
let initial = states->Array.get(0)
|
|
@@ -453,8 +459,7 @@ let lifecycleTopologyFindings = (
|
|
|
453
459
|
findings
|
|
454
460
|
->Array.push((
|
|
455
461
|
view,
|
|
456
|
-
`no command declares a transition INTO "${state}" — it is unreachable ` ++
|
|
457
|
-
`unless something outside this plugin's declarations puts a row there.`,
|
|
462
|
+
`no command declares a transition INTO "${state}" — it is unreachable ` ++ `unless something outside this plugin's declarations puts a row there.`,
|
|
458
463
|
))
|
|
459
464
|
->ignore
|
|
460
465
|
}
|
|
@@ -551,9 +556,7 @@ let labelFieldsFromStateSchema = (
|
|
|
551
556
|
// A variant's cross-entity references, shared by the command and event walks.
|
|
552
557
|
// `getFieldTarget`, not `getTarget`: an `array<string>` field declares it on the
|
|
553
558
|
// element schema.
|
|
554
|
-
let extractReferences = (properties: dict<S.t<unknown>>): array<
|
|
555
|
-
Reventless.Plugin.fieldReference,
|
|
556
|
-
> =>
|
|
559
|
+
let extractReferences = (properties: dict<S.t<unknown>>): array<Reventless.Plugin.fieldReference> =>
|
|
557
560
|
properties
|
|
558
561
|
->Dict.toArray
|
|
559
562
|
->Array.flatMap(((fieldName, fieldSchema)) =>
|
|
@@ -565,25 +568,26 @@ let extractReferences = (properties: dict<S.t<unknown>>): array<
|
|
|
565
568
|
Reventless.Reference.collectFieldTargets(fieldName, fieldSchema)->Array.map(((
|
|
566
569
|
path,
|
|
567
570
|
target,
|
|
568
|
-
)) =>
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}: Reventless.Plugin.fieldReference
|
|
574
|
-
))
|
|
571
|
+
)): Reventless.Plugin.fieldReference => {
|
|
572
|
+
Reventless.Plugin.fieldName: path,
|
|
573
|
+
entity: target.entity,
|
|
574
|
+
plugin: target.plugin,
|
|
575
|
+
})
|
|
575
576
|
)
|
|
576
577
|
|
|
577
578
|
let toEventDef = (v: S.t<unknown>): option<Reventless.Plugin.eventDef> => {
|
|
578
579
|
let mkDef = (~variantName, ~properties) => {
|
|
579
580
|
let references = extractReferences(properties)
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
581
|
+
|
|
582
|
+
(
|
|
583
|
+
{
|
|
584
|
+
Reventless.Plugin.name: variantName,
|
|
585
|
+
// Derived for the same reason as `commandDef.schema`: an event's field
|
|
586
|
+
// markers are the write side's half of the same vocabulary.
|
|
587
|
+
schema: v->SuryToJsonSchema.deriveObjectSchema->JSON.stringify,
|
|
588
|
+
references,
|
|
589
|
+
}: Reventless.Plugin.eventDef
|
|
590
|
+
)
|
|
587
591
|
}
|
|
588
592
|
switch v {
|
|
589
593
|
| Object({properties}) =>
|
|
@@ -611,9 +615,11 @@ let extractEventDefs = (eventSchema: S.t<unknown>): array<Reventless.Plugin.even
|
|
|
611
615
|
// Errors walk identically to events; only the def type differs, so a consumer
|
|
612
616
|
// can tell a refusal from a fact. Shared rather than copied, so they cannot drift.
|
|
613
617
|
let extractErrorDefs = (errorSchema: S.t<unknown>): array<Reventless.Plugin.errorDef> =>
|
|
614
|
-
extractEventDefs(errorSchema)->Array.map(({
|
|
615
|
-
|
|
616
|
-
|
|
618
|
+
extractEventDefs(errorSchema)->Array.map(({
|
|
619
|
+
name,
|
|
620
|
+
schema,
|
|
621
|
+
references,
|
|
622
|
+
}): Reventless.Plugin.errorDef => {name, schema, references})
|
|
617
623
|
|
|
618
624
|
// Module-level like the event walk: the synthetic Platform_Admin structure has no
|
|
619
625
|
// `module(Aggregate.T)` to hand `make`, and a second copy of this walk is what let
|
|
@@ -643,8 +649,7 @@ let commandLevelAndId = (~isAggregate, ~variantName, properties: dict<S.t<unknow
|
|
|
643
649
|
->Dict.toArray
|
|
644
650
|
->Array.filter(((fieldName, fieldSchema)) =>
|
|
645
651
|
fieldName != "TAG" &&
|
|
646
|
-
(Reventless.DcbTag.isTagged(fieldSchema) ||
|
|
647
|
-
Reventless.DcbTag.isTaggedArray(fieldSchema))
|
|
652
|
+
(Reventless.DcbTag.isTagged(fieldSchema) || Reventless.DcbTag.isTaggedArray(fieldSchema))
|
|
648
653
|
)
|
|
649
654
|
let taggedField =
|
|
650
655
|
taggedFields
|
|
@@ -727,10 +732,12 @@ let toCommandDef = (
|
|
|
727
732
|
// Evaluated against a synthetic value per constructor, the same shape the
|
|
728
733
|
// resolver builds at call time: a payload-bearing variant compiles to
|
|
729
734
|
// `{TAG, ...}`, a payload-less one to a bare string.
|
|
730
|
-
let syntheticCommand: unknown =
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
735
|
+
let syntheticCommand: unknown = Reventless.DcbTag.isVariantPayloadBearing(
|
|
736
|
+
parentSchema,
|
|
737
|
+
variantName,
|
|
738
|
+
)
|
|
739
|
+
? {"TAG": variantName}->Obj.magic
|
|
740
|
+
: variantName->Obj.magic
|
|
734
741
|
// The spec's own switch, which is exhaustive — so it also speaks for a
|
|
735
742
|
// constructor the host did not declare but spliced from a trait.
|
|
736
743
|
//
|
|
@@ -814,27 +821,30 @@ let toCommandDef = (
|
|
|
814
821
|
} else {
|
|
815
822
|
jsonSchema
|
|
816
823
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
824
|
+
|
|
825
|
+
(
|
|
826
|
+
{
|
|
827
|
+
Reventless.Plugin.name: variantName,
|
|
828
|
+
// The derived schema, not sury's raw one, which drops every `x-reventless-*`
|
|
829
|
+
// marker the PPX put on the fields. Also carries `x-reventless-graphql-type`.
|
|
830
|
+
schema: annotatedSchema->JSON.stringify,
|
|
831
|
+
level,
|
|
832
|
+
aggregateIdField,
|
|
833
|
+
// Empty for a `@noApi` variant: `mutationFieldFor` would resolve it to a
|
|
834
|
+
// sibling's field, which reads as callable. Exposed variants are unchanged.
|
|
835
|
+
mutationField,
|
|
836
|
+
references,
|
|
837
|
+
allowedStates,
|
|
838
|
+
targetState,
|
|
839
|
+
?allowedStatesSource,
|
|
840
|
+
apiExposed: Some(apiExposed),
|
|
841
|
+
requiredAccess,
|
|
842
|
+
// Resolved from this constructor's own properties, not the union's: two
|
|
843
|
+
// commands in one slice can disagree about whether they record an owner,
|
|
844
|
+
// and the write path stamps per constructor for the same reason.
|
|
845
|
+
ownerField: Reventless.Owner.fieldNamesOfProperties(properties)->Array.get(0),
|
|
846
|
+
}: Reventless.Plugin.commandDef
|
|
847
|
+
)
|
|
838
848
|
}
|
|
839
849
|
switch v {
|
|
840
850
|
| Object({properties}) =>
|
|
@@ -920,7 +930,7 @@ let queryableDefFromSpec = (
|
|
|
920
930
|
// the generated filter/order-by is built from cannot disagree.
|
|
921
931
|
let keyField = GraphQL_FragmentGenerator.resolveKeyField(~entityName=name, stateSchema)
|
|
922
932
|
{
|
|
923
|
-
Reventless.Plugin.name
|
|
933
|
+
Reventless.Plugin.name,
|
|
924
934
|
queryField: qf.listFieldName,
|
|
925
935
|
schema: stateSchema->SuryToJsonSchema.deriveObjectSchema->JSON.stringify,
|
|
926
936
|
consumedEventTypes,
|
|
@@ -946,7 +956,9 @@ let make = (
|
|
|
946
956
|
type api role,
|
|
947
957
|
~name: string,
|
|
948
958
|
~aggregates: array<module(ReventlessInfra.Aggregate.T with type api = api)>=[],
|
|
949
|
-
~readModels: array<
|
|
959
|
+
~readModels: array<
|
|
960
|
+
module(ReventlessInfra.ReadModel.T with type api = api and type role = role),
|
|
961
|
+
>=[],
|
|
950
962
|
~stateViewSlices: array<module(ReventlessInfra.StateViewSlice.T)>=[],
|
|
951
963
|
~stateChangeSlices: array<module(ReventlessInfra.StateChangeSlice.T)>=[],
|
|
952
964
|
~automationSlices: array<module(ReventlessInfra.AutomationSlice.T)>=[],
|
|
@@ -1011,8 +1023,7 @@ let make = (
|
|
|
1011
1023
|
|
|
1012
1024
|
// ── Cross-reference helpers ────────────────────────────────────────────────
|
|
1013
1025
|
|
|
1014
|
-
let intersects = (a: array<string>, b: array<string>) =>
|
|
1015
|
-
a->Array.some(x => b->Array.includes(x))
|
|
1026
|
+
let intersects = (a: array<string>, b: array<string>) => a->Array.some(x => b->Array.includes(x))
|
|
1016
1027
|
|
|
1017
1028
|
let linkedSvsFor = (producedTypes: array<string>): array<string> =>
|
|
1018
1029
|
svsConsumed->Array.filterMap(((viewName, consumed)) =>
|
|
@@ -1055,7 +1066,11 @@ let make = (
|
|
|
1055
1066
|
->Array.filter(((_, overlap, _)) => overlap > 0)
|
|
1056
1067
|
->Array.toSorted(((_, a, aTotal), (_, b, bTotal)) => {
|
|
1057
1068
|
let cmp = Int.compare(b, a)
|
|
1058
|
-
if cmp != Ordering.equal {
|
|
1069
|
+
if cmp != Ordering.equal {
|
|
1070
|
+
cmp
|
|
1071
|
+
} else {
|
|
1072
|
+
Int.compare(bTotal, aTotal)
|
|
1073
|
+
}
|
|
1059
1074
|
})
|
|
1060
1075
|
switch scored->Array.length {
|
|
1061
1076
|
| 0 => None
|
|
@@ -1168,18 +1183,17 @@ let make = (
|
|
|
1168
1183
|
dcbCommandOwners
|
|
1169
1184
|
->Dict.toArray
|
|
1170
1185
|
->Array.filter(((_, owners)) => owners->Array.length > 1)
|
|
1171
|
-
->Array.toSorted(((
|
|
1186
|
+
->Array.toSorted(((a, _), (b, _)) => String.compare(a, b))
|
|
1172
1187
|
if clashes->Array.length > 0 {
|
|
1173
1188
|
JsError.throwWithMessage(
|
|
1174
1189
|
`${name}: ` ++
|
|
1175
1190
|
clashes
|
|
1176
|
-
->Array.map(((
|
|
1191
|
+
->Array.map(((cmd, owners)) =>
|
|
1177
1192
|
`${owners->Array.join(" and ")} both declare the command "${cmd}"`
|
|
1178
1193
|
)
|
|
1179
1194
|
->Array.join("; ") ++
|
|
1180
1195
|
`.\n A DCB plugin routes a command by its bare name, so one of these would never run. ` ++
|
|
1181
|
-
`Qualify them — the shipped traits' emitters prefix every name with the host they are ` ++
|
|
1182
|
-
`grafted onto, for exactly this reason.`,
|
|
1196
|
+
`Qualify them — the shipped traits' emitters prefix every name with the host they are ` ++ `grafted onto, for exactly this reason.`,
|
|
1183
1197
|
)
|
|
1184
1198
|
}
|
|
1185
1199
|
|
|
@@ -1247,12 +1261,12 @@ let make = (
|
|
|
1247
1261
|
let requiredCapabilities =
|
|
1248
1262
|
outboundTranslationSlices
|
|
1249
1263
|
->Array.flatMap((module(OTS: ReventlessInfra.OutboundTranslationSlice.T)) =>
|
|
1250
|
-
OTS.Spec.capabilityNeeds->Array.map(
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
)
|
|
1264
|
+
OTS.Spec.capabilityNeeds->Array.map((
|
|
1265
|
+
need
|
|
1266
|
+
): Reventless.Plugin.requiredCapabilityDeclaration => {
|
|
1267
|
+
Reventless.Plugin.capability: Reventless.CapabilityNeed.toString(need),
|
|
1268
|
+
component: OTS.Spec.name,
|
|
1269
|
+
})
|
|
1256
1270
|
)
|
|
1257
1271
|
->Array.toSorted((a, b) =>
|
|
1258
1272
|
a.capability == b.capability
|
|
@@ -1272,14 +1286,12 @@ let make = (
|
|
|
1272
1286
|
// hand-typed string this whole mechanism exists to avoid. Sorted and
|
|
1273
1287
|
// deduplicated for the same reason the two declarations above are.
|
|
1274
1288
|
let traitDeclarations = {
|
|
1275
|
-
let entry = (~component, decl: Reventless.Trait.t) =>
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
}: Reventless.Plugin.traitDeclaration
|
|
1282
|
-
)
|
|
1289
|
+
let entry = (~component, decl: Reventless.Trait.t): Reventless.Plugin.traitDeclaration => {
|
|
1290
|
+
Reventless.Plugin.trait: decl.trait,
|
|
1291
|
+
version: decl.version,
|
|
1292
|
+
posture: Reventless.Trait.postureToString(decl.posture),
|
|
1293
|
+
component,
|
|
1294
|
+
}
|
|
1283
1295
|
[
|
|
1284
1296
|
aggregates->Array.flatMap((module(A: ReventlessInfra.Aggregate.T with type api = api)) =>
|
|
1285
1297
|
A.Spec.traits->Array.map(entry(~component=A.Spec.name, ...))
|
|
@@ -1293,7 +1305,9 @@ let make = (
|
|
|
1293
1305
|
]
|
|
1294
1306
|
->Array.flat
|
|
1295
1307
|
->Array.toSorted((a, b) =>
|
|
1296
|
-
a.trait == b.trait
|
|
1308
|
+
a.trait == b.trait
|
|
1309
|
+
? String.compare(a.component, b.component)
|
|
1310
|
+
: String.compare(a.trait, b.trait)
|
|
1297
1311
|
)
|
|
1298
1312
|
->Array.reduce([], (acc, d) =>
|
|
1299
1313
|
switch acc->Array.last {
|
|
@@ -1345,26 +1359,23 @@ let make = (
|
|
|
1345
1359
|
->GraphQL_FragmentGenerator.keyFieldGapMessage
|
|
1346
1360
|
->Option.forEach(why => log.warn(~comp="Plugin_Structure", `${name}/${entityName} ${why}`))
|
|
1347
1361
|
|
|
1348
|
-
let readModelDefs =
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
recordLifecycle(~entityName=R.Spec.name, stateSchema)
|
|
1366
|
-
recordKeyField(~entityName=R.Spec.name, stateSchema)
|
|
1367
|
-
({
|
|
1362
|
+
let readModelDefs = readModels->Array.map((
|
|
1363
|
+
module(R: ReventlessInfra.ReadModel.T with type api = api and type role = role),
|
|
1364
|
+
) => {
|
|
1365
|
+
let qf = Api_Naming.queryFieldNamesForReadModel(~plugin=name, ~name=R.Spec.name)
|
|
1366
|
+
let stateSchema = R.Spec.stateSchema->S.castToUnknown
|
|
1367
|
+
let label = labelFieldsFromStateSchema(~entityName=R.Spec.name, stateSchema)
|
|
1368
|
+
// The same call the capability deriver makes, so the published key and the
|
|
1369
|
+
// key the generated filter/order-by is built from cannot disagree.
|
|
1370
|
+
let keyField = GraphQL_FragmentGenerator.resolveKeyField(~entityName=R.Spec.name, stateSchema)
|
|
1371
|
+
// Qualified to the plugin's event ids so they match the producers' nodes.
|
|
1372
|
+
let consumed = qualify(~prefix=name, R.consumedEventNames)
|
|
1373
|
+
recordRetired(~entityName=R.Spec.name, stateSchema)
|
|
1374
|
+
recordLifecycle(~entityName=R.Spec.name, stateSchema)
|
|
1375
|
+
recordKeyField(~entityName=R.Spec.name, stateSchema)
|
|
1376
|
+
|
|
1377
|
+
(
|
|
1378
|
+
{
|
|
1368
1379
|
Reventless.Plugin.name: R.Spec.name,
|
|
1369
1380
|
queryField: qf.listFieldName,
|
|
1370
1381
|
schema: stateSchema->SuryToJsonSchema.deriveObjectSchema->JSON.stringify,
|
|
@@ -1389,8 +1400,9 @@ let make = (
|
|
|
1389
1400
|
idField: keyField->Option.map(((f, _)) => f),
|
|
1390
1401
|
idFieldSource: keyField->Option.map(((_, rung)) => rung),
|
|
1391
1402
|
requiredAccess: accessKeysFor(R.Spec.authorization),
|
|
1392
|
-
}: Reventless.Plugin.queryableDef
|
|
1393
|
-
|
|
1403
|
+
}: Reventless.Plugin.queryableDef
|
|
1404
|
+
)
|
|
1405
|
+
})
|
|
1394
1406
|
|
|
1395
1407
|
let stateViewDefs =
|
|
1396
1408
|
stateViewSlices->Array.mapWithIndex((module(SVS: ReventlessInfra.StateViewSlice.T), i) => {
|
|
@@ -1405,27 +1417,30 @@ let make = (
|
|
|
1405
1417
|
recordRetired(~entityName=SVS.Spec.name, stateSchema)
|
|
1406
1418
|
recordLifecycle(~entityName=SVS.Spec.name, stateSchema)
|
|
1407
1419
|
recordKeyField(~entityName=SVS.Spec.name, stateSchema)
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1420
|
+
|
|
1421
|
+
(
|
|
1422
|
+
{
|
|
1423
|
+
Reventless.Plugin.name: SVS.Spec.name,
|
|
1424
|
+
queryField: qf.listFieldName,
|
|
1425
|
+
schema: stateSchema->SuryToJsonSchema.deriveObjectSchema->JSON.stringify,
|
|
1426
|
+
consumedEventTypes: consumed,
|
|
1427
|
+
linkedWriteSide: linkedWriteSideFor(consumed),
|
|
1428
|
+
labelField: label.field,
|
|
1429
|
+
searchableFields: label.searchableFields,
|
|
1430
|
+
labelFieldSource: Some(labelFieldSourceToString(label.source)),
|
|
1431
|
+
lifecycleField: lifecycleFieldFromStateSchema(stateSchema),
|
|
1432
|
+
ownerField: Reventless.Owner.fieldNames(stateSchema)->Array.get(0),
|
|
1433
|
+
retiredField: retiredFieldFromStateSchema(stateSchema),
|
|
1434
|
+
retiredValues: retiredValuesFromStateSchema(stateSchema),
|
|
1435
|
+
namedWhenRetired: Some(namedWhenRetiredFromStateSchema(stateSchema)),
|
|
1436
|
+
visibility: visibilityTag(SVS.Spec.visibility),
|
|
1437
|
+
chapter: chapterOf(SVS.Spec.name),
|
|
1438
|
+
singleQueryField: Some(qf.singleFieldName),
|
|
1439
|
+
idField: keyField->Option.map(((f, _)) => f),
|
|
1440
|
+
idFieldSource: keyField->Option.map(((_, rung)) => rung),
|
|
1441
|
+
requiredAccess: accessKeysFor(SVS.Spec.authorization),
|
|
1442
|
+
}: Reventless.Plugin.queryableDef
|
|
1443
|
+
)
|
|
1429
1444
|
})
|
|
1430
1445
|
|
|
1431
1446
|
// ── Build writable defs ────────────────────────────────────────────────────
|
|
@@ -1434,43 +1449,52 @@ let make = (
|
|
|
1434
1449
|
stateChangeSlices->Array.mapWithIndex((module(SCS: ReventlessInfra.StateChangeSlice.T), i) => {
|
|
1435
1450
|
let (_, produced) = scsProduced->Array.getUnsafe(i)
|
|
1436
1451
|
let (_, consumed) = scsConsumed->Array.getUnsafe(i)
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1452
|
+
|
|
1453
|
+
(
|
|
1454
|
+
{
|
|
1455
|
+
Reventless.Plugin.name: SCS.Spec.name,
|
|
1456
|
+
commands: extractCommandDefs(
|
|
1457
|
+
~isAggregate=false,
|
|
1458
|
+
~mutationFieldFor=variantName =>
|
|
1459
|
+
Api_Naming.sliceMutationFieldFor(
|
|
1460
|
+
~plugin=name,
|
|
1461
|
+
~slice=SCS.Spec.name,
|
|
1462
|
+
~commandSchema=SCS.Spec.commandSchema->S.castToUnknown,
|
|
1463
|
+
~variant=variantName,
|
|
1464
|
+
),
|
|
1465
|
+
~commandAuthorization=SCS.Spec.commandAuthorization->Obj.magic,
|
|
1466
|
+
~commandTransition=SCS.Spec.commandTransition->Obj.magic,
|
|
1467
|
+
~derivedEdgeFor=derivedEdgeFor(~component=SCS.Spec.name, ...),
|
|
1468
|
+
SCS.Spec.commandSchema->S.castToUnknown,
|
|
1469
|
+
),
|
|
1470
|
+
producedEventTypes: produced,
|
|
1471
|
+
consumedEventTypes: consumed,
|
|
1472
|
+
linkedViews: linkedSvsFor(produced),
|
|
1473
|
+
consistencyRead: consistencyReadFor(consumed),
|
|
1474
|
+
events: extractEventDefs(SCS.Spec.eventSchema->S.castToUnknown),
|
|
1475
|
+
errors: extractErrorDefs(SCS.Spec.errorSchema->S.castToUnknown),
|
|
1476
|
+
chapter: chapterOf(SCS.Spec.name),
|
|
1477
|
+
}: Reventless.Plugin.writableDef
|
|
1478
|
+
)
|
|
1461
1479
|
})
|
|
1462
1480
|
|
|
1463
|
-
let aggregateDefs =
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
)
|
|
1468
|
-
|
|
1469
|
-
|
|
1481
|
+
let aggregateDefs = aggregates->Array.mapWithIndex((
|
|
1482
|
+
module(A: ReventlessInfra.Aggregate.T with type api = api),
|
|
1483
|
+
i,
|
|
1484
|
+
) => {
|
|
1485
|
+
let (_, produced) = aggProduced->Array.getUnsafe(i)
|
|
1486
|
+
|
|
1487
|
+
(
|
|
1488
|
+
{
|
|
1470
1489
|
Reventless.Plugin.name: A.Spec.name,
|
|
1471
1490
|
commands: extractCommandDefs(
|
|
1472
1491
|
~isAggregate=true,
|
|
1473
|
-
~mutationFieldFor=variantName =>
|
|
1492
|
+
~mutationFieldFor=variantName =>
|
|
1493
|
+
Api_Naming.aggregateMutationField(
|
|
1494
|
+
~plugin=name,
|
|
1495
|
+
~aggregate=A.Spec.name,
|
|
1496
|
+
~command=variantName,
|
|
1497
|
+
),
|
|
1474
1498
|
~commandAuthorization=A.Spec.commandAuthorization->Obj.magic,
|
|
1475
1499
|
~commandTransition=A.Spec.commandTransition->Obj.magic,
|
|
1476
1500
|
~derivedEdgeFor=derivedEdgeFor(~component=A.Spec.name, ...),
|
|
@@ -1483,8 +1507,9 @@ let make = (
|
|
|
1483
1507
|
events: extractEventDefs(A.Spec.eventSchema->S.castToUnknown),
|
|
1484
1508
|
errors: extractErrorDefs(A.Spec.errorSchema->S.castToUnknown),
|
|
1485
1509
|
chapter: chapterOf(A.Spec.name),
|
|
1486
|
-
}: Reventless.Plugin.writableDef
|
|
1487
|
-
|
|
1510
|
+
}: Reventless.Plugin.writableDef
|
|
1511
|
+
)
|
|
1512
|
+
})
|
|
1488
1513
|
|
|
1489
1514
|
// ── Automation slices ────────────────────────────────────────────────────────
|
|
1490
1515
|
|
|
@@ -1500,22 +1525,30 @@ let make = (
|
|
|
1500
1525
|
)
|
|
1501
1526
|
->Belt.Set.String.fromArray
|
|
1502
1527
|
->Belt.Set.String.toArray
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1528
|
+
|
|
1529
|
+
(
|
|
1530
|
+
{
|
|
1531
|
+
Reventless.Plugin.name: AS.Spec.name,
|
|
1532
|
+
consumedEventTypes: qualify(~prefix=name, allConsumedVariants),
|
|
1533
|
+
producedCommandTypes: qualify(~prefix=name, commandVariantNames(AS.Spec.commandSchema)),
|
|
1534
|
+
targetName: AS.Spec.targetName,
|
|
1535
|
+
chapter: chapterOf(AS.Spec.name),
|
|
1536
|
+
}: Reventless.Plugin.automationSliceDef
|
|
1537
|
+
)
|
|
1510
1538
|
})
|
|
1511
1539
|
|
|
1512
1540
|
// ── Outbound translation slices ───────────────────────────────────────────
|
|
1513
1541
|
|
|
1514
1542
|
let outboundTranslationSliceDefs =
|
|
1515
|
-
outboundTranslationSlices->Array.map((
|
|
1543
|
+
outboundTranslationSlices->Array.map((
|
|
1544
|
+
module(OTS: ReventlessInfra.OutboundTranslationSlice.T),
|
|
1545
|
+
): Reventless.Plugin.outboundTranslationSliceDef => {
|
|
1516
1546
|
Reventless.Plugin.name: OTS.Spec.name,
|
|
1517
1547
|
consumedEventTypes: qualify(~prefix=name, eventVariantNames(OTS.Spec.consumedEventSchema)),
|
|
1518
|
-
inboundCommandTypes: qualify(
|
|
1548
|
+
inboundCommandTypes: qualify(
|
|
1549
|
+
~prefix=name,
|
|
1550
|
+
commandVariantNames(OTS.Spec.inboundCommandSchema),
|
|
1551
|
+
),
|
|
1519
1552
|
targetName: OTS.Spec.targetName,
|
|
1520
1553
|
externalSystem: OTS.Spec.externalSystem,
|
|
1521
1554
|
chapter: chapterOf(OTS.Spec.name),
|
|
@@ -1523,18 +1556,20 @@ let make = (
|
|
|
1523
1556
|
// this plugin's own DCB log, and resolving it here to a derived name would
|
|
1524
1557
|
// be a second place that name is spelled.
|
|
1525
1558
|
consumedSources: Some(OTS.Spec.sourceNames),
|
|
1526
|
-
}
|
|
1559
|
+
})
|
|
1527
1560
|
|
|
1528
1561
|
// ── Inbound translation slices ────────────────────────────────────────────
|
|
1529
1562
|
|
|
1530
1563
|
let inboundTranslationSliceDefs =
|
|
1531
|
-
inboundTranslationSlices->Array.map((
|
|
1564
|
+
inboundTranslationSlices->Array.map((
|
|
1565
|
+
module(ITS: ReventlessInfra.InboundTranslationSlice.T),
|
|
1566
|
+
): Reventless.Plugin.inboundTranslationSliceDef => {
|
|
1532
1567
|
Reventless.Plugin.name: ITS.Spec.name,
|
|
1533
1568
|
commandTypes: qualify(~prefix=name, commandVariantNames(ITS.Spec.commandSchema)),
|
|
1534
1569
|
targetName: ITS.Spec.targetName,
|
|
1535
1570
|
externalSystem: ITS.Spec.externalSystem,
|
|
1536
1571
|
chapter: chapterOf(ITS.Spec.name),
|
|
1537
|
-
}
|
|
1572
|
+
})
|
|
1538
1573
|
|
|
1539
1574
|
// ── Extensions ───────────────────────────────────────────────────────────
|
|
1540
1575
|
|
|
@@ -1546,39 +1581,39 @@ let make = (
|
|
|
1546
1581
|
// what the author can write, not the payload-filtered subset the edges use.
|
|
1547
1582
|
let allVariantNames = schema => Reventless.DcbTag.extractAllVariantNames(schema)
|
|
1548
1583
|
|
|
1549
|
-
let extensionDefs =
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
E.
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1584
|
+
let extensionDefs = extensions->Array.map((module(E: ReventlessInfra.Extension.Blueprint)) => {
|
|
1585
|
+
let delegateNames = E.mappings->Array.map((module(M: E.Mapping)) => M.delegateName)
|
|
1586
|
+
let epEventNames = allVariantNames(E.Spec.eventSchema)
|
|
1587
|
+
let epCommandNames = allVariantNames(E.Spec.commandSchema)
|
|
1588
|
+
|
|
1589
|
+
// Mappings sharing one EP union their tables — the same event may route to
|
|
1590
|
+
// a different delegate's command in each.
|
|
1591
|
+
let commandsByEvent: Dict.t<array<string>> = Dict.make()
|
|
1592
|
+
let eventsByCommand: Dict.t<array<string>> = Dict.make()
|
|
1593
|
+
E.mappings->Array.forEach((module(M: E.Mapping)) => {
|
|
1594
|
+
let label = `${E.Spec.name} → ${M.delegateName}`
|
|
1595
|
+
pushAll(
|
|
1596
|
+
tableFailures,
|
|
1597
|
+
handledTableFailures(
|
|
1598
|
+
~label,
|
|
1599
|
+
~declared=M.handledEvents,
|
|
1600
|
+
~eventNames=epEventNames,
|
|
1601
|
+
~commandNames=Array.concat(M.delegateCommandNames, epCommandNames),
|
|
1602
|
+
),
|
|
1603
|
+
)
|
|
1604
|
+
pushAll(
|
|
1605
|
+
tableFailures,
|
|
1606
|
+
commandTableFailures(
|
|
1607
|
+
~label,
|
|
1608
|
+
~keyed="issuedCommands",
|
|
1609
|
+
~valueKind="comes from",
|
|
1610
|
+
~rows=M.issuedCommands->Array.map(({name, fromEventTypes}) => (name, fromEventTypes)),
|
|
1611
|
+
~keyNames=epCommandNames,
|
|
1612
|
+
~valueNames=M.delegateEventNames,
|
|
1613
|
+
),
|
|
1614
|
+
)
|
|
1615
|
+
M.issuedCommands->Array.forEach(
|
|
1616
|
+
({name: commandName, fromEventTypes}) => {
|
|
1582
1617
|
let key = `${E.Spec.name}.${commandName}`
|
|
1583
1618
|
eventsByCommand->Dict.set(
|
|
1584
1619
|
key,
|
|
@@ -1587,25 +1622,30 @@ let make = (
|
|
|
1587
1622
|
qualify(~prefix=name, fromEventTypes),
|
|
1588
1623
|
),
|
|
1589
1624
|
)
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1625
|
+
},
|
|
1626
|
+
)
|
|
1627
|
+
M.handledEvents->Array.forEach(
|
|
1628
|
+
({name: eventName, toCommandTypes}) => {
|
|
1592
1629
|
// The qualifier says which way the command goes: plugin-qualified
|
|
1593
1630
|
// inward, EP-qualified back to the port.
|
|
1594
1631
|
let qualified =
|
|
1595
|
-
toCommandTypes->Array.map(
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1632
|
+
toCommandTypes->Array.map(
|
|
1633
|
+
cmd =>
|
|
1634
|
+
M.delegateCommandNames->Array.includes(cmd)
|
|
1635
|
+
? `${name}.${cmd}`
|
|
1636
|
+
: `${E.Spec.name}.${cmd}`,
|
|
1599
1637
|
)
|
|
1600
1638
|
let key = `${E.Spec.name}.${eventName}`
|
|
1601
1639
|
commandsByEvent->Dict.set(
|
|
1602
1640
|
key,
|
|
1603
1641
|
Array.concat(commandsByEvent->Dict.get(key)->Option.getOr([]), qualified),
|
|
1604
1642
|
)
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1643
|
+
},
|
|
1644
|
+
)
|
|
1645
|
+
})
|
|
1607
1646
|
|
|
1608
|
-
|
|
1647
|
+
(
|
|
1648
|
+
{
|
|
1609
1649
|
Reventless.Plugin.name: E.Spec.name,
|
|
1610
1650
|
delegateNames,
|
|
1611
1651
|
eventTypes: qualify(~prefix=E.Spec.name, eventVariantNames(E.Spec.eventSchema)),
|
|
@@ -1613,21 +1653,22 @@ let make = (
|
|
|
1613
1653
|
handledEvents: Some(
|
|
1614
1654
|
commandsByEvent
|
|
1615
1655
|
->Dict.toArray
|
|
1616
|
-
->Array.map(((eventName, cmds)) =>
|
|
1656
|
+
->Array.map(((eventName, cmds)): Reventless.Plugin.handledEventDef => {
|
|
1617
1657
|
Reventless.Plugin.name: eventName,
|
|
1618
1658
|
toCommandTypes: dedupe(cmds),
|
|
1619
|
-
}
|
|
1659
|
+
}),
|
|
1620
1660
|
),
|
|
1621
1661
|
issuedCommands: Some(
|
|
1622
1662
|
eventsByCommand
|
|
1623
1663
|
->Dict.toArray
|
|
1624
|
-
->Array.map(((commandName, evs)) =>
|
|
1664
|
+
->Array.map(((commandName, evs)): Reventless.Plugin.issuedCommandDef => {
|
|
1625
1665
|
Reventless.Plugin.name: commandName,
|
|
1626
1666
|
fromEventTypes: dedupe(evs),
|
|
1627
|
-
}
|
|
1667
|
+
}),
|
|
1628
1668
|
),
|
|
1629
|
-
}: Reventless.Plugin.extensionDef
|
|
1630
|
-
|
|
1669
|
+
}: Reventless.Plugin.extensionDef
|
|
1670
|
+
)
|
|
1671
|
+
})
|
|
1631
1672
|
|
|
1632
1673
|
// ── Extension points (producer side) ──────────────────────────────────────
|
|
1633
1674
|
//
|
|
@@ -1646,14 +1687,21 @@ let make = (
|
|
|
1646
1687
|
|
|
1647
1688
|
extensionPoints->Array.forEach((module(M: ReventlessInfra.ExtensionPointMapping.Mapping)) => {
|
|
1648
1689
|
let epName = M.ExtensionPoint.name
|
|
1649
|
-
let sourceEvents = qualify(
|
|
1690
|
+
let sourceEvents = qualify(
|
|
1691
|
+
~prefix=name,
|
|
1692
|
+
eventVariantNames(M.Delegate.eventSchema->S.castToUnknown),
|
|
1693
|
+
)
|
|
1650
1694
|
// The EP's inbound command protocol (variants of its `command` type). Empty
|
|
1651
1695
|
// for a `command = unit` EP — an events-out-only boundary that routes nothing.
|
|
1652
1696
|
let commands = qualify(~prefix=epName, commandVariantNames(M.ExtensionPoint.commandSchema))
|
|
1653
1697
|
let (dels, evs, cmds) = epByName->Dict.get(epName)->Option.getOr(([], [], []))
|
|
1654
1698
|
epByName->Dict.set(
|
|
1655
1699
|
epName,
|
|
1656
|
-
(
|
|
1700
|
+
(
|
|
1701
|
+
Array.concat(dels, [M.Delegate.name]),
|
|
1702
|
+
Array.concat(evs, sourceEvents),
|
|
1703
|
+
Array.concat(cmds, commands),
|
|
1704
|
+
),
|
|
1657
1705
|
)
|
|
1658
1706
|
|
|
1659
1707
|
let label = `${epName} ← ${M.Delegate.name}`
|
|
@@ -1690,39 +1738,38 @@ let make = (
|
|
|
1690
1738
|
)
|
|
1691
1739
|
? Dict.fromArray([("TAG", JSON.Encode.string(cmd))])->JSON.Encode.object
|
|
1692
1740
|
: JSON.Encode.string(cmd)
|
|
1693
|
-
switch
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
}
|
|
1711
|
-
) {
|
|
1741
|
+
switch try {
|
|
1742
|
+
let command =
|
|
1743
|
+
Reventless.Message.fillMissingDefaults(
|
|
1744
|
+
M.ExtensionPoint.commandSchema,
|
|
1745
|
+
synthesised,
|
|
1746
|
+
[],
|
|
1747
|
+
)->Reventless.Util_Sury.fromJson(M.ExtensionPoint.commandSchema)
|
|
1748
|
+
let decodedAs =
|
|
1749
|
+
command
|
|
1750
|
+
->Reventless.Message.encode(M.ExtensionPoint.commandSchema)
|
|
1751
|
+
->Reventless.Message.variantNameOfJson
|
|
1752
|
+
decodedAs != cmd
|
|
1753
|
+
? Error(`a synthesised "${cmd}" decoded as "${decodedAs}"`)
|
|
1754
|
+
: Ok(M.mapIncomingCommand(probeId, command, probeMeta))
|
|
1755
|
+
} catch {
|
|
1756
|
+
| _ => Error(`the mapping raised on a synthesised "${cmd}"`)
|
|
1757
|
+
} {
|
|
1712
1758
|
| Ok(actions) =>
|
|
1713
|
-
actions->Array.forEach(
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1759
|
+
actions->Array.forEach(
|
|
1760
|
+
action =>
|
|
1761
|
+
switch action {
|
|
1762
|
+
| ReventlessInfra.ExtensionPointMapping.PublishCommand(_, routed) =>
|
|
1763
|
+
acceptedObserved
|
|
1764
|
+
->Array.push((
|
|
1765
|
+
cmd,
|
|
1766
|
+
routed
|
|
1767
|
+
->Reventless.Message.encode(M.Delegate.commandSchema)
|
|
1768
|
+
->Reventless.Message.variantNameOfJson,
|
|
1769
|
+
))
|
|
1770
|
+
->ignore
|
|
1771
|
+
| HandleDirective(_, _) => ()
|
|
1772
|
+
},
|
|
1726
1773
|
)
|
|
1727
1774
|
| Error(reason) =>
|
|
1728
1775
|
tableWarnings->Array.push(`${label}: not checked against the arms — ${reason}.`)->ignore
|
|
@@ -1731,8 +1778,8 @@ let make = (
|
|
|
1731
1778
|
acceptedObserved->Array.forEach(((cmd, routed)) =>
|
|
1732
1779
|
if (
|
|
1733
1780
|
!(
|
|
1734
|
-
M.acceptedCommands->Array.some(
|
|
1735
|
-
name == cmd && toCommandTypes->Array.includes(routed)
|
|
1781
|
+
M.acceptedCommands->Array.some(
|
|
1782
|
+
({name, toCommandTypes}) => name == cmd && toCommandTypes->Array.includes(routed),
|
|
1736
1783
|
)
|
|
1737
1784
|
)
|
|
1738
1785
|
) {
|
|
@@ -1764,8 +1811,7 @@ let make = (
|
|
|
1764
1811
|
->Array.push(
|
|
1765
1812
|
`${label}: publishedEvents declares ${M.publishedEvents
|
|
1766
1813
|
->Array.length
|
|
1767
|
-
->Int.toString} event(s), but the mapping has no mapOutgoingEvent and ` ++
|
|
1768
|
-
`publishes nothing.`,
|
|
1814
|
+
->Int.toString} event(s), but the mapping has no mapOutgoingEvent and ` ++ `publishes nothing.`,
|
|
1769
1815
|
)
|
|
1770
1816
|
->ignore
|
|
1771
1817
|
}
|
|
@@ -1785,8 +1831,11 @@ let make = (
|
|
|
1785
1831
|
// Filled directly rather than through `parseJsonTolerant`, which warns
|
|
1786
1832
|
// about inventing values — here the invention is the point.
|
|
1787
1833
|
let event =
|
|
1788
|
-
Reventless.Message.fillMissingDefaults(
|
|
1789
|
-
|
|
1834
|
+
Reventless.Message.fillMissingDefaults(
|
|
1835
|
+
M.Delegate.eventSchema,
|
|
1836
|
+
synthesised,
|
|
1837
|
+
[],
|
|
1838
|
+
)->Reventless.Util_Sury.fromJson(M.Delegate.eventSchema)
|
|
1790
1839
|
// A fabricated payload can decode as a sibling constructor; only a
|
|
1791
1840
|
// value that round-trips is judged.
|
|
1792
1841
|
let decodedAs =
|
|
@@ -1797,26 +1846,28 @@ let make = (
|
|
|
1797
1846
|
Error(`a synthesised "${src}" decoded as "${decodedAs}"`)
|
|
1798
1847
|
} else {
|
|
1799
1848
|
let actions = mapOutgoing(probeId, event, probeMeta, probeQueryEngine)
|
|
1800
|
-
actions->Array.forEach(
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1849
|
+
actions->Array.forEach(
|
|
1850
|
+
action =>
|
|
1851
|
+
switch action {
|
|
1852
|
+
| ReventlessInfra.ExtensionPointMapping.PublishEvent(_, published) =>
|
|
1853
|
+
observed
|
|
1854
|
+
->Array.push((
|
|
1855
|
+
src,
|
|
1856
|
+
published
|
|
1857
|
+
->Reventless.Message.encode(M.ExtensionPoint.eventSchema)
|
|
1858
|
+
->Reventless.Message.variantNameOfJson,
|
|
1859
|
+
))
|
|
1860
|
+
->ignore
|
|
1861
|
+
| PublishEventAsync(_) | HandleDirective(_, _) => ()
|
|
1862
|
+
},
|
|
1813
1863
|
)
|
|
1814
1864
|
// A promise hides what it will publish — leave the arm unjudged.
|
|
1815
|
-
actions->Array.some(
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1865
|
+
actions->Array.some(
|
|
1866
|
+
action =>
|
|
1867
|
+
switch action {
|
|
1868
|
+
| PublishEventAsync(_) => true
|
|
1869
|
+
| PublishEvent(_, _) | HandleDirective(_, _) => false
|
|
1870
|
+
},
|
|
1820
1871
|
)
|
|
1821
1872
|
? Error(`"${src}" publishes behind a promise`)
|
|
1822
1873
|
: Ok()
|
|
@@ -1857,10 +1908,7 @@ let make = (
|
|
|
1857
1908
|
let key = `${epName}.${published}`
|
|
1858
1909
|
table->Dict.set(
|
|
1859
1910
|
key,
|
|
1860
|
-
Array.concat(
|
|
1861
|
-
table->Dict.get(key)->Option.getOr([]),
|
|
1862
|
-
qualify(~prefix=name, fromEventTypes),
|
|
1863
|
-
),
|
|
1911
|
+
Array.concat(table->Dict.get(key)->Option.getOr([]), qualify(~prefix=name, fromEventTypes)),
|
|
1864
1912
|
)
|
|
1865
1913
|
})
|
|
1866
1914
|
epPublished->Dict.set(epName, table)
|
|
@@ -1869,7 +1917,7 @@ let make = (
|
|
|
1869
1917
|
let extensionPointDefs =
|
|
1870
1918
|
epByName
|
|
1871
1919
|
->Dict.toArray
|
|
1872
|
-
->Array.map(((epName, (dels, evs, cmds))) =>
|
|
1920
|
+
->Array.map(((epName, (dels, evs, cmds))): Reventless.Plugin.extensionPointDef => {
|
|
1873
1921
|
Reventless.Plugin.name: epName,
|
|
1874
1922
|
delegateNames: dedupe(dels),
|
|
1875
1923
|
sourceEventTypes: dedupe(evs),
|
|
@@ -1879,22 +1927,22 @@ let make = (
|
|
|
1879
1927
|
->Dict.get(epName)
|
|
1880
1928
|
->Option.getOr(Dict.make())
|
|
1881
1929
|
->Dict.toArray
|
|
1882
|
-
->Array.map(((published, sources)) =>
|
|
1930
|
+
->Array.map(((published, sources)): Reventless.Plugin.publishedEventDef => {
|
|
1883
1931
|
Reventless.Plugin.name: published,
|
|
1884
1932
|
fromEventTypes: dedupe(sources),
|
|
1885
|
-
}
|
|
1933
|
+
}),
|
|
1886
1934
|
),
|
|
1887
1935
|
acceptedCommands: Some(
|
|
1888
1936
|
epAccepted
|
|
1889
1937
|
->Dict.get(epName)
|
|
1890
1938
|
->Option.getOr(Dict.make())
|
|
1891
1939
|
->Dict.toArray
|
|
1892
|
-
->Array.map(((accepted, routed)) =>
|
|
1940
|
+
->Array.map(((accepted, routed)): Reventless.Plugin.acceptedCommandDef => {
|
|
1893
1941
|
Reventless.Plugin.name: accepted,
|
|
1894
1942
|
toCommandTypes: dedupe(routed),
|
|
1895
|
-
}
|
|
1943
|
+
}),
|
|
1896
1944
|
),
|
|
1897
|
-
}
|
|
1945
|
+
})
|
|
1898
1946
|
|
|
1899
1947
|
// Dead inbound surface, judged only after every mapping on an EP has been seen:
|
|
1900
1948
|
// one port's inbound protocol is split across its mappings, so a command the
|
|
@@ -1902,16 +1950,14 @@ let make = (
|
|
|
1902
1950
|
epByName
|
|
1903
1951
|
->Dict.toArray
|
|
1904
1952
|
->Array.forEach(((epName, (_, _, cmds))) => {
|
|
1905
|
-
let handled =
|
|
1906
|
-
epAccepted->Dict.get(epName)->Option.getOr(Dict.make())->Dict.keysToArray
|
|
1953
|
+
let handled = epAccepted->Dict.get(epName)->Option.getOr(Dict.make())->Dict.keysToArray
|
|
1907
1954
|
cmds
|
|
1908
1955
|
->dedupe
|
|
1909
1956
|
->Array.forEach(cmd =>
|
|
1910
1957
|
if !(handled->Array.includes(cmd)) {
|
|
1911
1958
|
tableWarnings
|
|
1912
1959
|
->Array.push(
|
|
1913
|
-
`${epName}: the extension point accepts "${cmd}", which no arm handles — a ` ++
|
|
1914
|
-
`sender gets no error and nothing happens.`,
|
|
1960
|
+
`${epName}: the extension point accepts "${cmd}", which no arm handles — a ` ++ `sender gets no error and nothing happens.`,
|
|
1915
1961
|
)
|
|
1916
1962
|
->ignore
|
|
1917
1963
|
}
|