@reventlessdev/reventless-core 3.0.0-alpha.300 → 3.0.0-alpha.302
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +3 -3
- package/package.json +10 -10
- package/src/{Behavior.res → Decision.res} +1 -1
- package/src/Message.res +1 -1
- package/src/ResourceAttribution.res +8 -8
- package/src/adapter/Auth/Auth_ActiveRole.res +1 -1
- package/src/adapter/Auth/Auth_RefusalVocabulary.res +1 -1
- package/src/adapter/EventLogProvisioning/EventLogProvisioning.res +1 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder.res +2 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +10 -5
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +2 -2
- package/src/adapter/Runtime/EventCollectorRuntime_Builder.res +1 -1
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +8 -3
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder.res +1 -1
- package/src/adapter/Runtime/PluginRuntime_Builder.res +3 -3
- package/src/adapter/Runtime/Runtime.res +10 -9
- package/src/adapter/Runtime/TaskRuntime_Builder.res +1 -1
- package/src/adapter/RuntimeExtension/RuntimeExtension.res +3 -3
- package/src/admin/Platform_Admin_Structure.res +3 -3
- package/src/admin/Platform_Admin_Structure.res.mjs +1 -1
- package/src/admin/Platform_BakedManifest.res +8 -8
- package/src/admin/Platform_BakedManifest.res.mjs +2 -2
- package/src/admin/Platform_ComponentDefinitionsApi.res +12 -12
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +11 -11
- package/src/admin/Platform_PluginStructuresApi.res +7 -7
- package/src/admin/Platform_PluginStructuresApi.res.mjs +4 -4
- package/src/admin/UiFragmentRegistry/StateChange/{UiFragmentRegistry_Behavior.res → UiFragmentRegistry_Decision.res} +1 -1
- package/src/admin/UiFragmentRegistry/StateChange/{UiFragmentRegistry_Behavior.res.mjs → UiFragmentRegistry_Decision.res.mjs} +1 -1
- package/src/admin/UiFragmentRegistry/StateView/UiFragments.res.mjs +1 -4
- package/src/components/Aggregate/Aggregate_Builder.res +2 -2
- package/src/components/Aggregate/Aggregate_Builder.res.mjs +2 -2
- package/src/components/Aggregate/Aggregate_Callback.res +17 -17
- package/src/components/Aggregate/Aggregate_Callback.res.mjs +11 -11
- package/src/components/Api/ApiNoApiHelpers.res +1 -1
- package/src/components/Api/GraphQL_FragmentGenerator.res +1 -1
- package/src/components/Api/SchemaType.res +1 -1
- package/src/components/Api/SuryToJsonSchema.res +1 -1
- package/src/components/CommandGenerator/CommandGenerator.res +1 -1
- package/src/components/CommandGenerator/CommandGenerator_Adapter.res +6 -1
- package/src/components/CommandTopic/CommandTopic.res +2 -2
- package/src/components/CommandTopic/CommandTopic_Adapter.res +1 -1
- package/src/components/CommandTopic/CommandTopic_Helpers.res +4 -4
- package/src/components/EventCollector/EventCollector.res +1 -1
- package/src/components/EventCollector/EventCollector_Adapter.res +1 -1
- package/src/components/EventLog/EventLog_Operations.res +2 -2
- package/src/components/Heartbeat/Heartbeat.res +1 -1
- package/src/components/ReadModel/ReadModel.res +1 -1
- package/src/components/StateChangeSlice/StateChangeSlice.res +1 -1
- package/src/components/StateChangeSlice/StateChangeSlice_Builder.res +3 -3
- package/src/components/StateChangeSlice/StateChangeSlice_Builder.res.mjs +3 -3
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +9 -9
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +4 -4
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +2 -2
- package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +1 -1
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +2 -2
- package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +1 -1
- package/src/components/Task/Task_Adapter.res +1 -1
- package/src/components/Task/Task_Builder.res +1 -1
- package/src/plugin/component/Plugin_Builder.res +7 -7
- package/src/plugin/component/Plugin_Builder.res.mjs +3 -3
- package/src/plugin/component/Plugin_BuiltHook.res +1 -1
- package/src/plugin/component/Plugin_Structure.res +19 -19
- package/src/plugin/component/Plugin_Structure.res.mjs +8 -8
- package/src/plugin/lifecycle/{PluginBehavior.res → PluginDecision.res} +4 -4
- package/src/plugin/lifecycle/{PluginBehavior.res.mjs → PluginDecision.res.mjs} +1 -1
- package/tests/admin/AdminApiSchemaDriftTest.res +8 -8
- package/tests/admin/AdminApiSchemaDriftTest.res.mjs +54 -54
- package/tests/admin/Platform_Admin_StructureTest.res +10 -10
- package/tests/admin/Platform_Admin_StructureTest.res.mjs +5 -5
- package/tests/admin/Platform_BakedManifestTest.res +7 -7
- package/tests/admin/Platform_BakedManifestTest.res.mjs +9 -9
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +11 -11
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +5 -5
- package/tests/admin/Platform_PluginStructuresApiTest.res +3 -3
- package/tests/aggregate/AggregateCacheTest.res +4 -4
- package/tests/aggregate/AggregateCacheTest.res.mjs +2 -2
- package/tests/aggregate/AggregateCallbackTest.res +2 -2
- package/tests/aggregate/AggregateCallbackTest.res.mjs +1 -1
- package/tests/aggregate/AggregateConflictTest.res +4 -4
- package/tests/aggregate/AggregateConflictTest.res.mjs +2 -2
- package/tests/aggregate/AggregateFixtures.res +5 -5
- package/tests/aggregate/AggregateFixtures.res.mjs +2 -2
- package/tests/aggregate/AggregateRejectionTest.res +1 -1
- package/tests/aggregate/AggregateSnapshotTest.res +7 -7
- package/tests/aggregate/AggregateSnapshotTest.res.mjs +4 -4
- package/tests/api/PsInternalFieldsReadModel.res +1 -1
- package/tests/api/TaggedUnionSchemaTest.res +1 -1
- package/tests/commandgenerator/CommandGeneratorCallbackTest.res +1 -1
- package/tests/commandgenerator/CommandGeneratorFixtures.res +2 -2
- package/tests/commandgenerator/CommandGeneratorFixtures.res.mjs +2 -2
- package/tests/commandgenerator/OwnerActingTest.res +4 -4
- package/tests/commandgenerator/OwnerActingTest.res.mjs +4 -4
- package/tests/commandgenerator/OwnerStampingTest.res +1 -1
- package/tests/commandtopic/CommandOutcomeHookTest.res +2 -2
- package/tests/commandtopic/CommandOutcomeHookTest.res.mjs +3 -3
- package/tests/dcb/DcbStateChangeSliceTest.res +5 -5
- package/tests/dcb/DcbStateChangeSliceTest.res.mjs +5 -5
- package/tests/message/MessageTest.res +1 -1
- package/tests/plugin/ManifestVisibilityTest.res +2 -2
- package/tests/plugin/PluginStructureAccessTest.res +1 -1
- package/tests/plugin/PluginStructureAccessTest.res.mjs +4 -4
- package/tests/plugin/PluginStructureTest.res +24 -24
- package/tests/plugin/PluginStructureTest.res.mjs +27 -27
- package/tests/plugin/StateView/PsAnnotatedView.res +1 -1
- package/tests/plugin/StateView/PsAnnotatedView.res.mjs +1 -4
- package/tests/plugin/StateView/PsAvailableProductsView.res.mjs +1 -4
- package/tests/plugin/StateView/PsCategoriesView.res.mjs +1 -4
- package/tests/plugin/StateView/PsCustomersView.res.mjs +1 -4
- package/tests/plugin/StateView/PsGatedView.res.mjs +1 -4
- package/tests/plugin/StateView/PsOrdersView.res.mjs +1 -4
- package/tests/plugin/StateView/PsShipmentsView.res.mjs +1 -4
- package/tests/projection/LifecycleTrailTest.res +1 -1
- /package/src/{Behavior.res.mjs → Decision.res.mjs} +0 -0
|
@@ -16,8 +16,8 @@ import * as StateChangeSlice_Callback$ReventlessCore from "./StateChangeSlice_Ca
|
|
|
16
16
|
let log = Logger$ReventlessCore.fromEnv();
|
|
17
17
|
|
|
18
18
|
function Make(Spec) {
|
|
19
|
-
return
|
|
20
|
-
let Callback = StateChangeSlice_Callback$ReventlessCore.Make(Spec)(
|
|
19
|
+
return Decision => {
|
|
20
|
+
let Callback = StateChangeSlice_Callback$ReventlessCore.Make(Spec)(Decision);
|
|
21
21
|
let makeJsonHandler = (tagKeysByEventType, crossPartitionTagKeys, partitionTag, dcbEventLogOps) => (stream => {
|
|
22
22
|
let decodedStream = Stream.flatMap(Stream.mapEffect(stream, param => {
|
|
23
23
|
let reference = param.reference;
|
|
@@ -67,7 +67,7 @@ function Make(Spec) {
|
|
|
67
67
|
};
|
|
68
68
|
return {
|
|
69
69
|
Spec: Spec,
|
|
70
|
-
|
|
70
|
+
Decision: Decision,
|
|
71
71
|
isAsync: false,
|
|
72
72
|
make: make
|
|
73
73
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module type T = {
|
|
2
2
|
module Spec: Reventless.StateChangeSlice.Spec
|
|
3
|
-
module
|
|
3
|
+
module Decision: Reventless.StateChangeSlice.Decision with module Spec := Spec
|
|
4
4
|
let handleCommands: (
|
|
5
5
|
~tagKeysByEventType: Dict.t<array<string>>=?,
|
|
6
6
|
~crossPartitionTagKeys: array<string>=?,
|
|
@@ -21,10 +21,10 @@ module type T = {
|
|
|
21
21
|
|
|
22
22
|
module Make = (
|
|
23
23
|
Spec: Reventless.StateChangeSlice.Spec,
|
|
24
|
-
|
|
25
|
-
): (T with module Spec = Spec and module
|
|
24
|
+
Decision: Reventless.StateChangeSlice.Decision with module Spec := Spec,
|
|
25
|
+
): (T with module Spec = Spec and module Decision := Decision) => {
|
|
26
26
|
module Spec = Spec
|
|
27
|
-
module
|
|
27
|
+
module Decision = Decision
|
|
28
28
|
|
|
29
29
|
let comp = `StateChangeSlice(${Spec.name})`
|
|
30
30
|
|
|
@@ -164,7 +164,7 @@ module Make = (
|
|
|
164
164
|
// sees the event that recorded them.
|
|
165
165
|
let projectionCache: Lru.t<
|
|
166
166
|
string,
|
|
167
|
-
(
|
|
167
|
+
(Decision.state, option<Reventless.DcbTag.sequencePosition>, array<string>),
|
|
168
168
|
> = Lru.make(~capacity=projectionCacheCapacity)
|
|
169
169
|
|
|
170
170
|
let resetCache = () => projectionCache->Lru.clear
|
|
@@ -174,7 +174,7 @@ module Make = (
|
|
|
174
174
|
// model — seeded from the projection cache when warm, so only events after
|
|
175
175
|
// the cached head are read (delta), else the full history (cold)
|
|
176
176
|
// 2. Decodes raw events using consumedEventSchema
|
|
177
|
-
// 3. Calls
|
|
177
|
+
// 3. Calls Decision.decide to produce new events
|
|
178
178
|
// 4. Encodes produced events to raw and appends with a condition
|
|
179
179
|
// 5. On conflict, retries from step 1 (up to maxRetries)
|
|
180
180
|
let handleSingleCommand = (
|
|
@@ -303,7 +303,7 @@ module Make = (
|
|
|
303
303
|
let rec attempt = (~retries) => {
|
|
304
304
|
let (baseState, afterPos, baseOwners) = switch seed.contents {
|
|
305
305
|
| Some((state, head, owners)) => (state, head, owners)
|
|
306
|
-
| None => (
|
|
306
|
+
| None => (Decision.initialState, None, [])
|
|
307
307
|
}
|
|
308
308
|
let cacheHit = seed.contents->Option.isSome
|
|
309
309
|
|
|
@@ -347,7 +347,7 @@ module Make = (
|
|
|
347
347
|
(dm, _pos, reads, owners),
|
|
348
348
|
(event, position, eventType, eventId, (ownerFields, data)),
|
|
349
349
|
) => (
|
|
350
|
-
|
|
350
|
+
Decision.evolve(dm, event),
|
|
351
351
|
Some(position),
|
|
352
352
|
reads->Array.concat([
|
|
353
353
|
switch eventId {
|
|
@@ -406,7 +406,7 @@ module Make = (
|
|
|
406
406
|
isDefect ? EffectLogger.logError(~comp, line) : EffectLogger.logWarn(~comp, line)
|
|
407
407
|
)->Effect.map(_ => Ok("rejected"))
|
|
408
408
|
| None =>
|
|
409
|
-
switch
|
|
409
|
+
switch Decision.decide(state, command'.command) {
|
|
410
410
|
| Ok(newEvents) if newEvents->Array.length == 0 =>
|
|
411
411
|
// No append, but the read snapshot is valid — cache it for the next command.
|
|
412
412
|
cachePut((state, headPosition, owners))
|
|
@@ -21,7 +21,7 @@ import * as EffectLogger$ReventlessCore from "../../util/EffectLogger.res.mjs";
|
|
|
21
21
|
import * as CommandTopic_Helpers$ReventlessCore from "../CommandTopic/CommandTopic_Helpers.res.mjs";
|
|
22
22
|
|
|
23
23
|
function Make(Spec) {
|
|
24
|
-
return
|
|
24
|
+
return Decision => {
|
|
25
25
|
let comp = `StateChangeSlice(` + Spec.name + `)`;
|
|
26
26
|
let decoder = DcbDecode$Reventless.makeDecoder(Spec.consumedEventSchema);
|
|
27
27
|
let queryEventTypes = decoder.eventTypes;
|
|
@@ -155,7 +155,7 @@ function Make(Spec) {
|
|
|
155
155
|
match[1],
|
|
156
156
|
match[2]
|
|
157
157
|
] : [
|
|
158
|
-
|
|
158
|
+
Decision.initialState,
|
|
159
159
|
undefined,
|
|
160
160
|
[]
|
|
161
161
|
];
|
|
@@ -202,7 +202,7 @@ function Make(Spec) {
|
|
|
202
202
|
let eventId = param$1[3];
|
|
203
203
|
let eventType = param$1[2];
|
|
204
204
|
return [
|
|
205
|
-
|
|
205
|
+
Decision.evolve(param[0], param$1[0]),
|
|
206
206
|
param$1[1],
|
|
207
207
|
param[2].concat([eventId !== undefined ? LogFormat$ReventlessCore.bold(eventType) + `(` + eventId + `)` : LogFormat$ReventlessCore.bold(eventType)]),
|
|
208
208
|
OwnerScope$Reventless.recordOwners(param[3], match[0], match[1])
|
|
@@ -237,7 +237,7 @@ function Make(Spec) {
|
|
|
237
237
|
_0: "rejected"
|
|
238
238
|
}));
|
|
239
239
|
}
|
|
240
|
-
let newEvents =
|
|
240
|
+
let newEvents = Decision.decide(state, command$p.command);
|
|
241
241
|
if (newEvents.TAG === "Ok") {
|
|
242
242
|
let newEvents$1 = newEvents._0;
|
|
243
243
|
if (newEvents$1.length === 0) {
|
|
@@ -109,7 +109,7 @@ function Make(RuntimeEnvironment) {
|
|
|
109
109
|
event: Primitive_option.valFromOption(event),
|
|
110
110
|
meta: meta,
|
|
111
111
|
recordedAt: recordedAt
|
|
112
|
-
}).map(__x => Projection$Reventless.mapActionId(__x, Spec.
|
|
112
|
+
}).map(__x => Projection$Reventless.mapActionId(__x, Spec.Id.toString, Spec.Id.makeFromString));
|
|
113
113
|
} catch (raw_exn) {
|
|
114
114
|
let exn$1 = Primitive_exceptions.internalToException(raw_exn);
|
|
115
115
|
let errMsg = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn$1), Stdlib_JsExn.message), "unknown");
|
|
@@ -44,8 +44,8 @@ module Make = (
|
|
|
44
44
|
Projection.project({event, meta: raw.meta, recordedAt: raw.recordedAt})->Array.map(
|
|
45
45
|
Reventless.Projection.mapActionId(
|
|
46
46
|
_,
|
|
47
|
-
~to_=Spec.
|
|
48
|
-
~from=Spec.
|
|
47
|
+
~to_=Spec.Id.toString,
|
|
48
|
+
~from=Spec.Id.makeFromString,
|
|
49
49
|
),
|
|
50
50
|
)
|
|
51
51
|
let actionsStr = LogFormat.actionNames(actions)
|
|
@@ -33,7 +33,7 @@ function Make(Spec) {
|
|
|
33
33
|
event: event,
|
|
34
34
|
meta: raw.meta,
|
|
35
35
|
recordedAt: raw.recordedAt
|
|
36
|
-
}).map(__x => Projection$Reventless.mapActionId(__x, Spec.
|
|
36
|
+
}).map(__x => Projection$Reventless.mapActionId(__x, Spec.Id.toString, Spec.Id.makeFromString));
|
|
37
37
|
let actionsStr = LogFormat$ReventlessCore.actionNames(actions);
|
|
38
38
|
Effect.runSync(EffectLogger$ReventlessCore.logInfo(comp, raw.data, `handling event ` + idx.contents.toString() + `/` + count + `: ` + LogFormat$ReventlessCore.bold(raw.eventType) + `(` + id + `) ` + actionsStr));
|
|
39
39
|
allActions.push(...actions.map(__x => Projection$ReventlessCore.rewriteAction(__x, raw.meta.time, Spec.stateSchema)));
|
|
@@ -24,7 +24,7 @@ module type Bucket = {
|
|
|
24
24
|
type bucketParts
|
|
25
25
|
|
|
26
26
|
let connect: connect<bucketParts, runtimeParts>
|
|
27
|
-
let makeHandler: Task.bucketCallback => Runtime.
|
|
27
|
+
let makeHandler: Task.bucketCallback => Runtime.invocationHandler<
|
|
28
28
|
callbackEvent,
|
|
29
29
|
context,
|
|
30
30
|
array<Task.taskAction>,
|
|
@@ -97,7 +97,7 @@ module Make = (
|
|
|
97
97
|
): Task.T => {
|
|
98
98
|
module Spec = Spec
|
|
99
99
|
type component = Task.component
|
|
100
|
-
// type handler = Runtime.
|
|
100
|
+
// type handler = Runtime.invocationHandler<
|
|
101
101
|
// TaskRuntimeBuilder.callbackEvent,
|
|
102
102
|
// TaskRuntimeBuilder.context,
|
|
103
103
|
// array<Task.taskAction>,
|
|
@@ -1050,31 +1050,31 @@ module Make = (
|
|
|
1050
1050
|
~aggregatePositions: array<string>=[],
|
|
1051
1051
|
): Reventless.Plugin.uiFragmentManifest => {
|
|
1052
1052
|
// Internal queryable components are hidden from the AutoUI manifest. They
|
|
1053
|
-
// remain fully wired (resolvers, GraphQL schema,
|
|
1053
|
+
// remain fully wired (resolvers, GraphQL schema, read-side defs,
|
|
1054
1054
|
// authorization) — visibility is a UX hint, not a security boundary. See
|
|
1055
1055
|
// docs/plans/done/component-visibility-annotations.md
|
|
1056
|
-
let
|
|
1056
|
+
let isVisibleReadSide = (q: Reventless.Plugin.readSideDef) =>
|
|
1057
1057
|
switch q.visibility {
|
|
1058
1058
|
| Some("Internal") => false
|
|
1059
1059
|
| _ => true
|
|
1060
1060
|
}
|
|
1061
|
-
let visibleReadModels = pluginStructure.readModels->Array.filter(
|
|
1062
|
-
let visibleStateViewSlices = pluginStructure.stateViewSlices->Array.filter(
|
|
1063
|
-
let listPanel = (q: Reventless.Plugin.
|
|
1061
|
+
let visibleReadModels = pluginStructure.readModels->Array.filter(isVisibleReadSide)
|
|
1062
|
+
let visibleStateViewSlices = pluginStructure.stateViewSlices->Array.filter(isVisibleReadSide)
|
|
1063
|
+
let listPanel = (q: Reventless.Plugin.readSideDef): Reventless.Plugin.panelManifestEntry => {
|
|
1064
1064
|
fragmentId: name ++ "." ++ q.name ++ ".list",
|
|
1065
1065
|
title: q.name,
|
|
1066
1066
|
description: "",
|
|
1067
1067
|
positions: readModelPositions,
|
|
1068
1068
|
requiredAccess: None,
|
|
1069
1069
|
}
|
|
1070
|
-
let detailPanel = (w: Reventless.Plugin.
|
|
1070
|
+
let detailPanel = (w: Reventless.Plugin.writeSideDef): Reventless.Plugin.panelManifestEntry => {
|
|
1071
1071
|
fragmentId: name ++ "." ++ w.name ++ ".detail",
|
|
1072
1072
|
title: w.name,
|
|
1073
1073
|
description: "",
|
|
1074
1074
|
positions: aggregatePositions,
|
|
1075
1075
|
requiredAccess: None,
|
|
1076
1076
|
}
|
|
1077
|
-
let listPage = (q: Reventless.Plugin.
|
|
1077
|
+
let listPage = (q: Reventless.Plugin.readSideDef): Reventless.Plugin.pageManifestEntry => {
|
|
1078
1078
|
fragmentId: name ++ "." ++ q.name ++ ".list",
|
|
1079
1079
|
title: q.name,
|
|
1080
1080
|
menuEntry: {label: q.name, icon: None, group: None, sortOrder: 0},
|
|
@@ -47,7 +47,7 @@ function Make(Spec) {
|
|
|
47
47
|
let makeAutoUIManifest = (remoteEntryUrl, name, pluginStructure, readModelPositionsOpt, aggregatePositionsOpt) => {
|
|
48
48
|
let readModelPositions = readModelPositionsOpt !== undefined ? readModelPositionsOpt : [];
|
|
49
49
|
let aggregatePositions = aggregatePositionsOpt !== undefined ? aggregatePositionsOpt : [];
|
|
50
|
-
let
|
|
50
|
+
let isVisibleReadSide = q => {
|
|
51
51
|
let match = q.visibility;
|
|
52
52
|
if (match !== undefined) {
|
|
53
53
|
return match !== "Internal";
|
|
@@ -55,8 +55,8 @@ function Make(Spec) {
|
|
|
55
55
|
return true;
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
|
-
let visibleReadModels = pluginStructure.readModels.filter(
|
|
59
|
-
let visibleStateViewSlices = pluginStructure.stateViewSlices.filter(
|
|
58
|
+
let visibleReadModels = pluginStructure.readModels.filter(isVisibleReadSide);
|
|
59
|
+
let visibleStateViewSlices = pluginStructure.stateViewSlices.filter(isVisibleReadSide);
|
|
60
60
|
let listPanel = q => ({
|
|
61
61
|
fragmentId: name + "." + q.name + ".list",
|
|
62
62
|
title: q.name,
|
|
@@ -97,7 +97,7 @@ type pluginDeployedSchema = {
|
|
|
97
97
|
/** Intra-plugin grouping band (the "chapter") this component belongs to, mirrored
|
|
98
98
|
from the static `pluginStructure` for parity on the hook surface. Absent when the
|
|
99
99
|
component carries no chapter (lives directly under a kind-folder). See
|
|
100
|
-
`Reventless.Plugin.
|
|
100
|
+
`Reventless.Plugin.readSideDef.chapter`. */
|
|
101
101
|
chapter?: string,
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -74,7 +74,7 @@ type retiredCheck =
|
|
|
74
74
|
// says whether that name is a case of the field's enum). A name the enum does not
|
|
75
75
|
// declare is a failure — the predicate then matches no row and every row stays
|
|
76
76
|
// visible. A `value` on a field that is not the lifecycle is only a warning: a
|
|
77
|
-
//
|
|
77
|
+
// modeling judgement rather than a wrong name.
|
|
78
78
|
let checkRetiredValue = (~entityName: string, stateSchema: S.t<unknown>): retiredCheck =>
|
|
79
79
|
switch retiredFromStateSchema(stateSchema) {
|
|
80
80
|
// The boolean form names no state, so there is nothing to compare. Not a skip.
|
|
@@ -392,13 +392,13 @@ let extensionPartitionFailure = (
|
|
|
392
392
|
// forces a spec to pick its linked view's enum.
|
|
393
393
|
let checkDeclaredTransitions = (
|
|
394
394
|
~pluginName: string,
|
|
395
|
-
~
|
|
395
|
+
~writeSides: array<Reventless.Plugin.writeSideDef>,
|
|
396
396
|
~lifecycleStatesByView: dict<array<string>>,
|
|
397
397
|
): unit => {
|
|
398
398
|
let unvalidated = ref(0)
|
|
399
399
|
let failures = []
|
|
400
400
|
|
|
401
|
-
|
|
401
|
+
writeSides->Array.forEach(w =>
|
|
402
402
|
w.commands->Array.forEach(cmd => {
|
|
403
403
|
let declared = Array.concat(
|
|
404
404
|
cmd.allowedStates->Option.getOr([]),
|
|
@@ -469,7 +469,7 @@ let checkDeclaredTransitions = (
|
|
|
469
469
|
// and one that stops a deploy gets silenced rather than fixed. Pure, so the rule
|
|
470
470
|
// tests without a log; `checkLifecycleTopology` reports.
|
|
471
471
|
let lifecycleTopologyFindings = (
|
|
472
|
-
~
|
|
472
|
+
~writeSides: array<Reventless.Plugin.writeSideDef>,
|
|
473
473
|
~lifecycleStatesByView: dict<array<string>>,
|
|
474
474
|
): array<(string, string)> => {
|
|
475
475
|
let findings = []
|
|
@@ -479,7 +479,7 @@ let lifecycleTopologyFindings = (
|
|
|
479
479
|
// Arrival only, so a creating command (a target and no from-set) counts
|
|
480
480
|
// towards reachability like any other edge.
|
|
481
481
|
let reachable =
|
|
482
|
-
|
|
482
|
+
writeSides->Array.reduce([], (acc, w) =>
|
|
483
483
|
w.linkedViews->Array.includes(view)
|
|
484
484
|
? Array.concat(acc, w.commands->Array.filterMap(cmd => cmd.targetState))
|
|
485
485
|
: acc
|
|
@@ -508,10 +508,10 @@ let lifecycleTopologyFindings = (
|
|
|
508
508
|
|
|
509
509
|
let checkLifecycleTopology = (
|
|
510
510
|
~pluginName: string,
|
|
511
|
-
~
|
|
511
|
+
~writeSides: array<Reventless.Plugin.writeSideDef>,
|
|
512
512
|
~lifecycleStatesByView: dict<array<string>>,
|
|
513
513
|
): unit =>
|
|
514
|
-
lifecycleTopologyFindings(~
|
|
514
|
+
lifecycleTopologyFindings(~writeSides, ~lifecycleStatesByView)->Array.forEach(((view, message)) =>
|
|
515
515
|
log.warn(~comp="Plugin_Structure", `${pluginName}/${view}: ${message}`)
|
|
516
516
|
)
|
|
517
517
|
|
|
@@ -1074,12 +1074,12 @@ let visibilityTag = (v: Reventless.Visibility.t): option<string> =>
|
|
|
1074
1074
|
}
|
|
1075
1075
|
|
|
1076
1076
|
/**
|
|
1077
|
-
A read model's `
|
|
1077
|
+
A read model's `readSideDef` assembled from its spec, for the platform's own
|
|
1078
1078
|
components: `make` takes `module(ReadModel.T)` values, and at structure-assembly
|
|
1079
1079
|
time the platform has no built module to hand itself. Every extractor here is the
|
|
1080
1080
|
one `make` calls, so the two cannot drift — a hand-written record did, four times.
|
|
1081
1081
|
*/
|
|
1082
|
-
let
|
|
1082
|
+
let readSideDefFromSpec = (
|
|
1083
1083
|
~plugin: string,
|
|
1084
1084
|
~name: string,
|
|
1085
1085
|
~stateSchema: S.t<unknown>,
|
|
@@ -1088,7 +1088,7 @@ let queryableDefFromSpec = (
|
|
|
1088
1088
|
~consumedEventTypes: array<string>=[],
|
|
1089
1089
|
~linkedWriteSide: array<string>=[],
|
|
1090
1090
|
~chapter: option<string>=?,
|
|
1091
|
-
): Reventless.Plugin.
|
|
1091
|
+
): Reventless.Plugin.readSideDef => {
|
|
1092
1092
|
let qf = Api_Naming.queryFieldNamesForReadModel(~plugin, ~name)
|
|
1093
1093
|
let label = labelFieldsFromStateSchema(~entityName=name, stateSchema)
|
|
1094
1094
|
// The published key is always filterable; not every filterable field is the key.
|
|
@@ -1498,9 +1498,9 @@ let make = (
|
|
|
1498
1498
|
)
|
|
1499
1499
|
)
|
|
1500
1500
|
|
|
1501
|
-
// ── Build
|
|
1501
|
+
// ── Build read-side defs ───────────────────────────────────────────────────
|
|
1502
1502
|
//
|
|
1503
|
-
// Internal views are carried here, tagged via `
|
|
1503
|
+
// Internal views are carried here, tagged via `readSideDef.visibility`, so
|
|
1504
1504
|
// developer tools see them; AutoUI's consumers re-filter on the tag.
|
|
1505
1505
|
// View name -> the states its lifecycle field can hold, collected as the view
|
|
1506
1506
|
// defs are built so the transition check below has both sides in one place.
|
|
@@ -1576,7 +1576,7 @@ let make = (
|
|
|
1576
1576
|
idFieldSource: keyField->Option.map(((_, rung)) => rung),
|
|
1577
1577
|
requiredAccess: accessKeysFor(R.Spec.authorization->Reventless.Authorization.named),
|
|
1578
1578
|
requiredRoles: ?rolesFor(R.Spec.authorization->Reventless.Authorization.named),
|
|
1579
|
-
}: Reventless.Plugin.
|
|
1579
|
+
}: Reventless.Plugin.readSideDef
|
|
1580
1580
|
)
|
|
1581
1581
|
})
|
|
1582
1582
|
|
|
@@ -1617,11 +1617,11 @@ let make = (
|
|
|
1617
1617
|
idFieldSource: keyField->Option.map(((_, rung)) => rung),
|
|
1618
1618
|
requiredAccess: accessKeysFor(SVS.Spec.authorization->Reventless.Authorization.named),
|
|
1619
1619
|
requiredRoles: ?rolesFor(SVS.Spec.authorization->Reventless.Authorization.named),
|
|
1620
|
-
}: Reventless.Plugin.
|
|
1620
|
+
}: Reventless.Plugin.readSideDef
|
|
1621
1621
|
)
|
|
1622
1622
|
})
|
|
1623
1623
|
|
|
1624
|
-
// ── Build
|
|
1624
|
+
// ── Build write-side defs ──────────────────────────────────────────────────
|
|
1625
1625
|
|
|
1626
1626
|
// Non-throwing: this is a description of the plugin, and the builder is what
|
|
1627
1627
|
// refuses a boundary whose partitions do not resolve.
|
|
@@ -1728,7 +1728,7 @@ let make = (
|
|
|
1728
1728
|
events: extractEventDefs(~identityViews, SCS.Spec.eventSchema->S.castToUnknown),
|
|
1729
1729
|
errors: extractErrorDefs(SCS.Spec.errorSchema->S.castToUnknown),
|
|
1730
1730
|
chapter: chapterOf(SCS.Spec.name),
|
|
1731
|
-
}: Reventless.Plugin.
|
|
1731
|
+
}: Reventless.Plugin.writeSideDef
|
|
1732
1732
|
)
|
|
1733
1733
|
})
|
|
1734
1734
|
|
|
@@ -1762,7 +1762,7 @@ let make = (
|
|
|
1762
1762
|
events: extractEventDefs(~identityViews, A.Spec.eventSchema->S.castToUnknown),
|
|
1763
1763
|
errors: extractErrorDefs(A.Spec.errorSchema->S.castToUnknown),
|
|
1764
1764
|
chapter: chapterOf(A.Spec.name),
|
|
1765
|
-
}: Reventless.Plugin.
|
|
1765
|
+
}: Reventless.Plugin.writeSideDef
|
|
1766
1766
|
)
|
|
1767
1767
|
})
|
|
1768
1768
|
|
|
@@ -2258,12 +2258,12 @@ let make = (
|
|
|
2258
2258
|
// assembled, so the check cannot run inline where the defs are made.
|
|
2259
2259
|
checkDeclaredTransitions(
|
|
2260
2260
|
~pluginName=name,
|
|
2261
|
-
~
|
|
2261
|
+
~writeSides=Array.concat(stateChangeDefs, aggregateDefs),
|
|
2262
2262
|
~lifecycleStatesByView,
|
|
2263
2263
|
)
|
|
2264
2264
|
checkLifecycleTopology(
|
|
2265
2265
|
~pluginName=name,
|
|
2266
|
-
~
|
|
2266
|
+
~writeSides=Array.concat(stateChangeDefs, aggregateDefs),
|
|
2267
2267
|
~lifecycleStatesByView,
|
|
2268
2268
|
)
|
|
2269
2269
|
// Also a second pass, for a different reason: the failures are gathered per
|
|
@@ -318,12 +318,12 @@ function extensionPartitionFailure(label, delegate, expected, actual) {
|
|
|
318
318
|
return label + `: the extension ` + seen + `, but the plugin partitions ` + delegate + ` by ` + describePartition(expected) + `. An extension sees only the slice's command and events, not what it reads or its chapter — ` + (`add @partitionTag ` + describePartition(expected) + ` to ` + delegate + `'s event.`);
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
function checkDeclaredTransitions(pluginName,
|
|
321
|
+
function checkDeclaredTransitions(pluginName, writeSides, lifecycleStatesByView) {
|
|
322
322
|
let unvalidated = {
|
|
323
323
|
contents: 0
|
|
324
324
|
};
|
|
325
325
|
let failures = [];
|
|
326
|
-
|
|
326
|
+
writeSides.forEach(w => {
|
|
327
327
|
w.commands.forEach(cmd => {
|
|
328
328
|
let t = cmd.targetState;
|
|
329
329
|
let declared = Stdlib_Option.getOr(cmd.allowedStates, []).concat(t !== undefined ? [t] : []);
|
|
@@ -357,12 +357,12 @@ function checkDeclaredTransitions(pluginName, writables, lifecycleStatesByView)
|
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
function lifecycleTopologyFindings(
|
|
360
|
+
function lifecycleTopologyFindings(writeSides, lifecycleStatesByView) {
|
|
361
361
|
let findings = [];
|
|
362
362
|
Object.entries(lifecycleStatesByView).forEach(param => {
|
|
363
363
|
let states = param[1];
|
|
364
364
|
let view = param[0];
|
|
365
|
-
let reachable = Stdlib_Array.reduce(
|
|
365
|
+
let reachable = Stdlib_Array.reduce(writeSides, [], (acc, w) => {
|
|
366
366
|
if (w.linkedViews.includes(view)) {
|
|
367
367
|
return acc.concat(Stdlib_Array.filterMap(w.commands, cmd => cmd.targetState));
|
|
368
368
|
} else {
|
|
@@ -386,8 +386,8 @@ function lifecycleTopologyFindings(writables, lifecycleStatesByView) {
|
|
|
386
386
|
return findings;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
function checkLifecycleTopology(pluginName,
|
|
390
|
-
lifecycleTopologyFindings(
|
|
389
|
+
function checkLifecycleTopology(pluginName, writeSides, lifecycleStatesByView) {
|
|
390
|
+
lifecycleTopologyFindings(writeSides, lifecycleStatesByView).forEach(param => log.warn("Plugin_Structure", undefined, pluginName + `/` + param[0] + `: ` + param[1]));
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
function labelFieldSourceToString(s) {
|
|
@@ -772,7 +772,7 @@ function visibilityTag(v) {
|
|
|
772
772
|
}
|
|
773
773
|
}
|
|
774
774
|
|
|
775
|
-
function
|
|
775
|
+
function readSideDefFromSpec(plugin, name, stateSchema, authorization, visibilityOpt, consumedEventTypesOpt, linkedWriteSideOpt, chapter) {
|
|
776
776
|
let visibility = visibilityOpt !== undefined ? visibilityOpt : "Public";
|
|
777
777
|
let consumedEventTypes = consumedEventTypesOpt !== undefined ? consumedEventTypesOpt : [];
|
|
778
778
|
let linkedWriteSide = linkedWriteSideOpt !== undefined ? linkedWriteSideOpt : [];
|
|
@@ -1591,7 +1591,7 @@ export {
|
|
|
1591
1591
|
toCommandDef,
|
|
1592
1592
|
extractCommandDefs,
|
|
1593
1593
|
visibilityTag,
|
|
1594
|
-
|
|
1594
|
+
readSideDefFromSpec,
|
|
1595
1595
|
make,
|
|
1596
1596
|
}
|
|
1597
1597
|
/* log Not a pure module */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
@@reventless.
|
|
1
|
+
@@reventless.decision(PluginSpec)
|
|
2
2
|
|
|
3
3
|
open Reventless.Plugin
|
|
4
4
|
|
|
@@ -30,9 +30,9 @@ type state = {
|
|
|
30
30
|
|
|
31
31
|
let initialState = {current: None, known: Dict.make()}
|
|
32
32
|
|
|
33
|
-
// The @@reventless.
|
|
34
|
-
// this framework-internal
|
|
35
|
-
//
|
|
33
|
+
// The @@reventless.decision snapshot injection is gated to Aggregate/ folders;
|
|
34
|
+
// this framework-internal decision lives under plugin/lifecycle, so the
|
|
35
|
+
// Decision.T field is satisfied manually. The admin Plugin aggregate's history
|
|
36
36
|
// is short (lifecycle transitions), so persisted snapshots would buy nothing.
|
|
37
37
|
let snapshot = None
|
|
38
38
|
|
|
@@ -382,7 +382,7 @@ let snapshot;
|
|
|
382
382
|
|
|
383
383
|
let atomicCounter;
|
|
384
384
|
|
|
385
|
-
let moduleUrl = "@reventlessdev/reventless-core/src/plugin/lifecycle/
|
|
385
|
+
let moduleUrl = "@reventlessdev/reventless-core/src/plugin/lifecycle/PluginDecision.res.mjs";
|
|
386
386
|
|
|
387
387
|
export {
|
|
388
388
|
Spec,
|
|
@@ -80,7 +80,7 @@ let cmd: commandDef = {
|
|
|
80
80
|
let evt: eventDef = {name: "ProductAdded", schema: "{}", references: [fieldRef]}
|
|
81
81
|
let err: errorDef = {name: "ProductAlreadyExists", schema: "{}", references: []}
|
|
82
82
|
|
|
83
|
-
let
|
|
83
|
+
let writeSide: writeSideDef = {
|
|
84
84
|
name: "Product",
|
|
85
85
|
commands: [cmd],
|
|
86
86
|
producedEventTypes: ["ProductAdded"],
|
|
@@ -92,7 +92,7 @@ let writable: writableDef = {
|
|
|
92
92
|
chapter: Some("Catalogue"),
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
let
|
|
95
|
+
let readSide: readSideDef = {
|
|
96
96
|
name: "Products",
|
|
97
97
|
queryField: "Catalog_Products",
|
|
98
98
|
schema: "{}",
|
|
@@ -174,10 +174,10 @@ let capabilityDecl: requiredCapabilityDeclaration = {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
let structure: pluginStructure = {
|
|
177
|
-
readModels: [
|
|
177
|
+
readModels: [readSide],
|
|
178
178
|
stateViewSlices: [],
|
|
179
179
|
stateChangeSlices: [],
|
|
180
|
-
aggregates: [
|
|
180
|
+
aggregates: [writeSide],
|
|
181
181
|
automationSlices: [automation],
|
|
182
182
|
outboundTranslationSlices: [outbound],
|
|
183
183
|
inboundTranslationSlices: [inbound],
|
|
@@ -278,16 +278,16 @@ let guards: array<guard> = [
|
|
|
278
278
|
{
|
|
279
279
|
gqlType: "Platform_WriteSideDef",
|
|
280
280
|
sdlTypes: componentSdl,
|
|
281
|
-
recordFields: schemaKeys(
|
|
282
|
-
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.
|
|
281
|
+
recordFields: schemaKeys(writeSideDefSchema),
|
|
282
|
+
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeWriteSideDef(writeSide)),
|
|
283
283
|
wireOnly: [],
|
|
284
284
|
recordOnly: [],
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
gqlType: "Platform_ReadSideDef",
|
|
288
288
|
sdlTypes: componentSdl,
|
|
289
|
-
recordFields: schemaKeys(
|
|
290
|
-
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.
|
|
289
|
+
recordFields: schemaKeys(readSideDefSchema),
|
|
290
|
+
wireFields: jsonKeys(Platform_ComponentDefinitionsApi.encodeReadSideDef(readSide)),
|
|
291
291
|
wireOnly: [],
|
|
292
292
|
// The roles behind `requiredAccess`, read by the deploy check in-process.
|
|
293
293
|
// A client gates on the mapped groups and has no use for role names.
|