@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
|
@@ -97,11 +97,11 @@ function encodeCommandDef(c) {
|
|
|
97
97
|
]);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function
|
|
100
|
+
function isPublicReadSide(q) {
|
|
101
101
|
return Primitive_object.notequal(q.visibility, "Internal");
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
function
|
|
104
|
+
function encodeReadSideDef(r) {
|
|
105
105
|
return Object.fromEntries([
|
|
106
106
|
[
|
|
107
107
|
"name",
|
|
@@ -216,7 +216,7 @@ function encodeErrorDef(e) {
|
|
|
216
216
|
]);
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
function
|
|
219
|
+
function encodeWriteSideDef(w) {
|
|
220
220
|
return Object.fromEntries([
|
|
221
221
|
[
|
|
222
222
|
"name",
|
|
@@ -399,23 +399,23 @@ function encodePluginStructureEntry(pluginId, derived, def) {
|
|
|
399
399
|
],
|
|
400
400
|
[
|
|
401
401
|
"readModels",
|
|
402
|
-
def.readModels.filter(
|
|
402
|
+
def.readModels.filter(isPublicReadSide).map(encodeReadSideDef)
|
|
403
403
|
],
|
|
404
404
|
[
|
|
405
405
|
"stateViewSlices",
|
|
406
|
-
def.stateViewSlices.filter(
|
|
406
|
+
def.stateViewSlices.filter(isPublicReadSide).map(encodeReadSideDef)
|
|
407
407
|
],
|
|
408
408
|
[
|
|
409
409
|
"internalQueryables",
|
|
410
|
-
def.readModels.concat(def.stateViewSlices).filter(q => !Primitive_object.notequal(q.visibility, "Internal")).map(
|
|
410
|
+
def.readModels.concat(def.stateViewSlices).filter(q => !Primitive_object.notequal(q.visibility, "Internal")).map(encodeReadSideDef)
|
|
411
411
|
],
|
|
412
412
|
[
|
|
413
413
|
"stateChangeSlices",
|
|
414
|
-
def.stateChangeSlices.map(
|
|
414
|
+
def.stateChangeSlices.map(encodeWriteSideDef)
|
|
415
415
|
],
|
|
416
416
|
[
|
|
417
417
|
"aggregates",
|
|
418
|
-
def.aggregates.map(
|
|
418
|
+
def.aggregates.map(encodeWriteSideDef)
|
|
419
419
|
],
|
|
420
420
|
[
|
|
421
421
|
"automationSlices",
|
|
@@ -448,11 +448,11 @@ export {
|
|
|
448
448
|
encodeStrings,
|
|
449
449
|
encodeFieldReference,
|
|
450
450
|
encodeCommandDef,
|
|
451
|
-
|
|
452
|
-
|
|
451
|
+
isPublicReadSide,
|
|
452
|
+
encodeReadSideDef,
|
|
453
453
|
encodeEventDef,
|
|
454
454
|
encodeErrorDef,
|
|
455
|
-
|
|
455
|
+
encodeWriteSideDef,
|
|
456
456
|
encodeAutomationSliceDef,
|
|
457
457
|
encodeOutboundTranslationSliceDef,
|
|
458
458
|
encodeInboundTranslationSliceDef,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// difference is not cosmetic.
|
|
7
7
|
//
|
|
8
8
|
// Platform_ComponentDefinitions the deployed AutoUI's menu, drill-down pages and
|
|
9
|
-
//
|
|
9
|
+
// read-side defs. Internal ReadModels /
|
|
10
10
|
// StateViewSlices are filtered OUT — showing them
|
|
11
11
|
// would put components in an end user's menu that
|
|
12
12
|
// the author marked as not-for-display.
|
|
@@ -43,7 +43,7 @@ let sdlTypes: array<string> = [
|
|
|
43
43
|
// reason the capability above is: a plugin built against a newer framework,
|
|
44
44
|
// naming a posture this schema has never heard of, still decodes.
|
|
45
45
|
//
|
|
46
|
-
// It records ORIGIN, never
|
|
46
|
+
// It records ORIGIN, never behavior — a grafted file is the host's to edit
|
|
47
47
|
// afterwards — so a reader must not paint a declared graft as a verified one.
|
|
48
48
|
`type Platform_TraitDeclaration {\n trait: String!\n version: String!\n posture: String!\n component: String!\n}`,
|
|
49
49
|
// `extensionPoints` / `requiredStores` / `requiredStoreDeclarations` /
|
|
@@ -119,7 +119,7 @@ let encodeRequiredCapabilityDeclaration = (d: requiredCapabilityDeclaration): JS
|
|
|
119
119
|
("component", JSON.Encode.string(d.component)),
|
|
120
120
|
])->JSON.Encode.object
|
|
121
121
|
|
|
122
|
-
// Deliberately NOT filtered by `
|
|
122
|
+
// Deliberately NOT filtered by `isPublicReadSide`: that filter is the AutoUI
|
|
123
123
|
// contract, and applying it here would silently drop Internal components from the
|
|
124
124
|
// graph a developer tool draws.
|
|
125
125
|
let encodePluginStructureEntry = (~pluginId: string, def: pluginStructure): JSON.t =>
|
|
@@ -128,25 +128,25 @@ let encodePluginStructureEntry = (~pluginId: string, def: pluginStructure): JSON
|
|
|
128
128
|
(
|
|
129
129
|
"readModels",
|
|
130
130
|
def.readModels
|
|
131
|
-
->Array.map(Platform_ComponentDefinitionsApi.
|
|
131
|
+
->Array.map(Platform_ComponentDefinitionsApi.encodeReadSideDef)
|
|
132
132
|
->JSON.Encode.array,
|
|
133
133
|
),
|
|
134
134
|
(
|
|
135
135
|
"stateViewSlices",
|
|
136
136
|
def.stateViewSlices
|
|
137
|
-
->Array.map(Platform_ComponentDefinitionsApi.
|
|
137
|
+
->Array.map(Platform_ComponentDefinitionsApi.encodeReadSideDef)
|
|
138
138
|
->JSON.Encode.array,
|
|
139
139
|
),
|
|
140
140
|
(
|
|
141
141
|
"stateChangeSlices",
|
|
142
142
|
def.stateChangeSlices
|
|
143
|
-
->Array.map(Platform_ComponentDefinitionsApi.
|
|
143
|
+
->Array.map(Platform_ComponentDefinitionsApi.encodeWriteSideDef)
|
|
144
144
|
->JSON.Encode.array,
|
|
145
145
|
),
|
|
146
146
|
(
|
|
147
147
|
"aggregates",
|
|
148
148
|
def.aggregates
|
|
149
|
-
->Array.map(Platform_ComponentDefinitionsApi.
|
|
149
|
+
->Array.map(Platform_ComponentDefinitionsApi.encodeWriteSideDef)
|
|
150
150
|
->JSON.Encode.array,
|
|
151
151
|
),
|
|
152
152
|
(
|
|
@@ -124,19 +124,19 @@ function encodePluginStructureEntry(pluginId, def) {
|
|
|
124
124
|
],
|
|
125
125
|
[
|
|
126
126
|
"readModels",
|
|
127
|
-
def.readModels.map(Platform_ComponentDefinitionsApi$ReventlessCore.
|
|
127
|
+
def.readModels.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeReadSideDef)
|
|
128
128
|
],
|
|
129
129
|
[
|
|
130
130
|
"stateViewSlices",
|
|
131
|
-
def.stateViewSlices.map(Platform_ComponentDefinitionsApi$ReventlessCore.
|
|
131
|
+
def.stateViewSlices.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeReadSideDef)
|
|
132
132
|
],
|
|
133
133
|
[
|
|
134
134
|
"stateChangeSlices",
|
|
135
|
-
def.stateChangeSlices.map(Platform_ComponentDefinitionsApi$ReventlessCore.
|
|
135
|
+
def.stateChangeSlices.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeWriteSideDef)
|
|
136
136
|
],
|
|
137
137
|
[
|
|
138
138
|
"aggregates",
|
|
139
|
-
def.aggregates.map(Platform_ComponentDefinitionsApi$ReventlessCore.
|
|
139
|
+
def.aggregates.map(Platform_ComponentDefinitionsApi$ReventlessCore.encodeWriteSideDef)
|
|
140
140
|
],
|
|
141
141
|
[
|
|
142
142
|
"automationSlices",
|
|
@@ -68,7 +68,7 @@ let Spec;
|
|
|
68
68
|
|
|
69
69
|
let initialState;
|
|
70
70
|
|
|
71
|
-
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChange/
|
|
71
|
+
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChange/UiFragmentRegistry_Decision.res.mjs";
|
|
72
72
|
|
|
73
73
|
export {
|
|
74
74
|
Spec,
|
|
@@ -40,8 +40,6 @@ let name = "UiFragments";
|
|
|
40
40
|
|
|
41
41
|
let Id;
|
|
42
42
|
|
|
43
|
-
let Key;
|
|
44
|
-
|
|
45
43
|
let subIdConfig;
|
|
46
44
|
|
|
47
45
|
let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateView/UiFragments.res.mjs";
|
|
@@ -52,11 +50,10 @@ let visibility = "Public";
|
|
|
52
50
|
|
|
53
51
|
export {
|
|
54
52
|
name,
|
|
55
|
-
Id,
|
|
56
53
|
stateSchema,
|
|
57
54
|
consumedEventSchema,
|
|
58
55
|
config,
|
|
59
|
-
|
|
56
|
+
Id,
|
|
60
57
|
subIdConfig,
|
|
61
58
|
moduleUrl,
|
|
62
59
|
authorization,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Make = (
|
|
2
2
|
Spec: Reventless.Aggregate.Spec,
|
|
3
|
-
|
|
3
|
+
Decision: Decision.T with module Spec := Spec,
|
|
4
4
|
EventMappings: EventMapper.Mappings with module Target := Spec,
|
|
5
5
|
RuntimeEnvironment: Runtime.Environment,
|
|
6
6
|
CommandGeneratorResolvers: CommandGenerator_Adapter.Resolvers
|
|
@@ -87,7 +87,7 @@ module Make = (
|
|
|
87
87
|
->Pulumi.Output.apply(eventLogOps => {
|
|
88
88
|
module AggregateCallback = Aggregate_Callback.Make(
|
|
89
89
|
Spec,
|
|
90
|
-
|
|
90
|
+
Decision,
|
|
91
91
|
{
|
|
92
92
|
module Spec = Spec
|
|
93
93
|
module EventLog = SpecificEventLog
|
|
@@ -16,7 +16,7 @@ import * as CommandGenerator_Builder$ReventlessCore from "../CommandGenerator/Co
|
|
|
16
16
|
import * as CommandGenerator_Callback$ReventlessCore from "../CommandGenerator/CommandGenerator_Callback.res.mjs";
|
|
17
17
|
|
|
18
18
|
function Make(Spec) {
|
|
19
|
-
return
|
|
19
|
+
return Decision => (EventMappings => (RuntimeEnvironment => (CommandGeneratorResolvers => (CommandTopicChannel => (EventLogStorage => (EventTopicPublisher => (EventCollectorChannel => (AggregateRuntimeBuilder => (HooksConfig => {
|
|
20
20
|
let SpecificEventLog = EventLog_Builder$ReventlessCore.Make(Spec)(EventLogStorage)(EventTopicPublisher);
|
|
21
21
|
let SpecificCommandTopic = CommandTopic_Builder$ReventlessCore.Make({
|
|
22
22
|
Id: Spec.Id,
|
|
@@ -36,7 +36,7 @@ function Make(Spec) {
|
|
|
36
36
|
name: Spec.name
|
|
37
37
|
}, opts);
|
|
38
38
|
return Component$ReventlessCore.operations(eventLog).apply(eventLogOps => {
|
|
39
|
-
let AggregateCallback = Aggregate_Callback$ReventlessCore.Make(Spec)(
|
|
39
|
+
let AggregateCallback = Aggregate_Callback$ReventlessCore.Make(Spec)(Decision)({
|
|
40
40
|
Spec: Spec,
|
|
41
41
|
EventLog: SpecificEventLog,
|
|
42
42
|
eventLog: eventLogOps
|
|
@@ -16,7 +16,7 @@ module type T = {
|
|
|
16
16
|
|
|
17
17
|
module Make = (
|
|
18
18
|
Spec: Reventless.Aggregate.Spec,
|
|
19
|
-
|
|
19
|
+
Decision: Decision.T with module Spec := Spec,
|
|
20
20
|
Ops: Ops with module Spec = Spec,
|
|
21
21
|
): (T with module Spec = Spec) => {
|
|
22
22
|
module Spec = Spec
|
|
@@ -82,9 +82,9 @@ module Make = (
|
|
|
82
82
|
| CmdRefused(CommandTopic_Helpers.ownershipRefusal)
|
|
83
83
|
|
|
84
84
|
// Folds a single command into the running accumulator: (currentState, perCommandOutcomes).
|
|
85
|
-
// A domain rejection from
|
|
85
|
+
// A domain rejection from Decision.decide is recorded as `CmdRejected` (no state change,
|
|
86
86
|
// surviving commands in the batch continue); a successful decide records `CmdOk(events)`
|
|
87
|
-
// and advances state via `
|
|
87
|
+
// and advances state via `Decision.evolve`.
|
|
88
88
|
//
|
|
89
89
|
// `~seq` is the replayed sequence number the batch decides from, carried in so
|
|
90
90
|
// the line below can name the state rather than serialise it.
|
|
@@ -94,7 +94,7 @@ module Make = (
|
|
|
94
94
|
let processCommand = (~seq: int) =>
|
|
95
95
|
(
|
|
96
96
|
(state, owners, outcomes): (
|
|
97
|
-
|
|
97
|
+
Decision.state,
|
|
98
98
|
array<string>,
|
|
99
99
|
array<(string, cmdOutcome, Message.meta)>,
|
|
100
100
|
),
|
|
@@ -131,9 +131,9 @@ module Make = (
|
|
|
131
131
|
)->Effect.runSync
|
|
132
132
|
(state, owners, Array.concat(outcomes, [(reference, CmdRefused(refused), meta)]))
|
|
133
133
|
| None =>
|
|
134
|
-
switch
|
|
134
|
+
switch Decision.decide(state, command'.command) {
|
|
135
135
|
| Ok(generatedEvents) =>
|
|
136
|
-
let newState = generatedEvents->Array.reduce(state,
|
|
136
|
+
let newState = generatedEvents->Array.reduce(state, Decision.evolve)
|
|
137
137
|
(
|
|
138
138
|
newState,
|
|
139
139
|
generatedEvents->Array.reduce(owners, recordEventOwners),
|
|
@@ -179,7 +179,7 @@ module Make = (
|
|
|
179
179
|
// the fixed capacity (a per-aggregate knob is a future refinement — see
|
|
180
180
|
// docs/plans/done/aggregate-snapshotting.md).
|
|
181
181
|
let replayCacheCapacity = 100
|
|
182
|
-
let replayCache: Lru.t<string, (
|
|
182
|
+
let replayCache: Lru.t<string, (Decision.state, int, array<string>)> = Lru.make(
|
|
183
183
|
~capacity=replayCacheCapacity,
|
|
184
184
|
)
|
|
185
185
|
|
|
@@ -195,8 +195,8 @@ module Make = (
|
|
|
195
195
|
// Off for an owner-marked aggregate: a snapshot holds the state alone, so a
|
|
196
196
|
// replay seeded from one would never see the event that recorded the owner,
|
|
197
197
|
// and the ownership check would pass every caller.
|
|
198
|
-
let snapshotConfig = actsOnOwned ? None :
|
|
199
|
-
if actsOnOwned &&
|
|
198
|
+
let snapshotConfig = actsOnOwned ? None : Decision.snapshot
|
|
199
|
+
if actsOnOwned && Decision.snapshot->Option.isSome {
|
|
200
200
|
EffectLogger.logWarn(
|
|
201
201
|
~comp,
|
|
202
202
|
"snapshots disabled: this aggregate records an @owner, which a snapshot does not carry",
|
|
@@ -223,7 +223,7 @@ module Make = (
|
|
|
223
223
|
// State ⇄ JSON via the configured stateSchema. Both swallow sury failures to
|
|
224
224
|
// `None` — an unserializable state just skips the snapshot write, and an
|
|
225
225
|
// undecodable snapshot falls back to full replay; neither can fail a command.
|
|
226
|
-
let encodeState = (state:
|
|
226
|
+
let encodeState = (state: Decision.state): option<JSON.t> =>
|
|
227
227
|
switch snapshotConfig {
|
|
228
228
|
| Some({stateSchema}) =>
|
|
229
229
|
switch state->Reventless.Util_Sury.toJson(stateSchema) {
|
|
@@ -233,7 +233,7 @@ module Make = (
|
|
|
233
233
|
| None => None
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
let decodeState = (json: JSON.t): option<
|
|
236
|
+
let decodeState = (json: JSON.t): option<Decision.state> =>
|
|
237
237
|
switch snapshotConfig {
|
|
238
238
|
| Some({stateSchema}) =>
|
|
239
239
|
switch json->Reventless.Util_Sury.fromJson(stateSchema) {
|
|
@@ -250,7 +250,7 @@ module Make = (
|
|
|
250
250
|
let coldReadState = id => {
|
|
251
251
|
let idStr = id->Spec.Id.toString
|
|
252
252
|
let seedEffect = switch snapshotConfig {
|
|
253
|
-
| None => Effect.succeed((
|
|
253
|
+
| None => Effect.succeed((Decision.initialState, 0))
|
|
254
254
|
| Some(_) =>
|
|
255
255
|
Effect.promise(() => Ops.eventLog.latestSnapshot(id))->Effect.map(snapResult =>
|
|
256
256
|
switch snapResult {
|
|
@@ -262,28 +262,28 @@ module Make = (
|
|
|
262
262
|
~comp,
|
|
263
263
|
`snapshot ignored (undecodable): id=${idStr}, seq=${snap.seqNr->Int.toString} — full replay`,
|
|
264
264
|
)->Effect.runSync
|
|
265
|
-
(
|
|
265
|
+
(Decision.initialState, 0)
|
|
266
266
|
}
|
|
267
267
|
| Ok(Some(snap)) =>
|
|
268
268
|
EffectLogger.logWarn(
|
|
269
269
|
~comp,
|
|
270
270
|
`snapshot ignored (schema drift): id=${idStr}, seq=${snap.EventLog.seqNr->Int.toString} — full replay`,
|
|
271
271
|
)->Effect.runSync
|
|
272
|
-
(
|
|
273
|
-
| Ok(None) => (
|
|
272
|
+
(Decision.initialState, 0)
|
|
273
|
+
| Ok(None) => (Decision.initialState, 0)
|
|
274
274
|
| Error(msg) =>
|
|
275
275
|
EffectLogger.logWarn(
|
|
276
276
|
~comp,
|
|
277
277
|
`snapshot read failed (ignored): id=${idStr}: ${msg} — full replay`,
|
|
278
278
|
)->Effect.runSync
|
|
279
|
-
(
|
|
279
|
+
(Decision.initialState, 0)
|
|
280
280
|
}
|
|
281
281
|
)
|
|
282
282
|
}
|
|
283
283
|
seedEffect->Effect.flatMap(((seedState, seedSeq)) =>
|
|
284
284
|
Ops.eventLog.replayStream(id, ~fromSeq=seedSeq)
|
|
285
285
|
->Stream.runFold((seedState, seedSeq, []), ((st, n, owners), ev) => (
|
|
286
|
-
|
|
286
|
+
Decision.evolve(st, ev),
|
|
287
287
|
n + 1,
|
|
288
288
|
owners->recordEventOwners(ev),
|
|
289
289
|
))
|
|
@@ -20,7 +20,7 @@ import * as SchemaWalker$ReventlessCore from "../../plugin/component/SchemaWalke
|
|
|
20
20
|
import * as CommandTopic_Helpers$ReventlessCore from "../CommandTopic/CommandTopic_Helpers.res.mjs";
|
|
21
21
|
|
|
22
22
|
function Make(Spec) {
|
|
23
|
-
return
|
|
23
|
+
return Decision => (Ops => {
|
|
24
24
|
let comp = `Aggregate(` + Spec.name + `)`;
|
|
25
25
|
let groupTopicItemsByIdStream = stream => Effect.map(Stream.runFold(stream, {}, (dict, item) => {
|
|
26
26
|
let id = Spec.Id.toString(item.command.id);
|
|
@@ -73,10 +73,10 @@ function Make(Spec) {
|
|
|
73
73
|
]])
|
|
74
74
|
];
|
|
75
75
|
}
|
|
76
|
-
let generatedEvents =
|
|
76
|
+
let generatedEvents = Decision.decide(state, command$p.command);
|
|
77
77
|
if (generatedEvents.TAG === "Ok") {
|
|
78
78
|
let generatedEvents$1 = generatedEvents._0;
|
|
79
|
-
let newState = Stdlib_Array.reduce(generatedEvents$1, state,
|
|
79
|
+
let newState = Stdlib_Array.reduce(generatedEvents$1, state, Decision.evolve);
|
|
80
80
|
return [
|
|
81
81
|
newState,
|
|
82
82
|
Stdlib_Array.reduce(generatedEvents$1, owners, recordEventOwners),
|
|
@@ -112,8 +112,8 @@ function Make(Spec) {
|
|
|
112
112
|
});
|
|
113
113
|
let replayCache = Lru$ReventlessCore.make(100);
|
|
114
114
|
let resetCache = () => Lru$ReventlessCore.clear(replayCache);
|
|
115
|
-
let snapshotConfig = actsOnOwned ? undefined :
|
|
116
|
-
if (actsOnOwned && Stdlib_Option.isSome(
|
|
115
|
+
let snapshotConfig = actsOnOwned ? undefined : Decision.snapshot;
|
|
116
|
+
if (actsOnOwned && Stdlib_Option.isSome(Decision.snapshot)) {
|
|
117
117
|
Effect.runSync(EffectLogger$ReventlessCore.logWarn(comp, undefined, "snapshots disabled: this aggregate records an @owner, which a snapshot does not carry"));
|
|
118
118
|
}
|
|
119
119
|
let stateSchemaHash = snapshotConfig !== undefined ? HashObject(Object.fromEntries([[
|
|
@@ -153,7 +153,7 @@ function Make(Spec) {
|
|
|
153
153
|
let snap = snapResult._0;
|
|
154
154
|
if (snap === undefined) {
|
|
155
155
|
return [
|
|
156
|
-
|
|
156
|
+
Decision.initialState,
|
|
157
157
|
0
|
|
158
158
|
];
|
|
159
159
|
}
|
|
@@ -167,24 +167,24 @@ function Make(Spec) {
|
|
|
167
167
|
} else {
|
|
168
168
|
Effect.runSync(EffectLogger$ReventlessCore.logWarn(comp, undefined, `snapshot ignored (undecodable): id=` + idStr + `, seq=` + snap.seqNr.toString() + ` — full replay`));
|
|
169
169
|
return [
|
|
170
|
-
|
|
170
|
+
Decision.initialState,
|
|
171
171
|
0
|
|
172
172
|
];
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
Effect.runSync(EffectLogger$ReventlessCore.logWarn(comp, undefined, `snapshot ignored (schema drift): id=` + idStr + `, seq=` + snap.seqNr.toString() + ` — full replay`));
|
|
176
176
|
return [
|
|
177
|
-
|
|
177
|
+
Decision.initialState,
|
|
178
178
|
0
|
|
179
179
|
];
|
|
180
180
|
}
|
|
181
181
|
Effect.runSync(EffectLogger$ReventlessCore.logWarn(comp, undefined, `snapshot read failed (ignored): id=` + idStr + `: ` + snapResult._0 + ` — full replay`));
|
|
182
182
|
return [
|
|
183
|
-
|
|
183
|
+
Decision.initialState,
|
|
184
184
|
0
|
|
185
185
|
];
|
|
186
186
|
}) : Effect.succeed([
|
|
187
|
-
|
|
187
|
+
Decision.initialState,
|
|
188
188
|
0
|
|
189
189
|
]);
|
|
190
190
|
return Effect.flatMap(seedEffect, param => {
|
|
@@ -194,7 +194,7 @@ function Make(Spec) {
|
|
|
194
194
|
seedSeq,
|
|
195
195
|
[]
|
|
196
196
|
], (param, ev) => [
|
|
197
|
-
|
|
197
|
+
Decision.evolve(param[0], ev),
|
|
198
198
|
param[1] + 1 | 0,
|
|
199
199
|
recordEventOwners(param[2], ev)
|
|
200
200
|
]), param => {
|
|
@@ -5,7 +5,7 @@ let isNoApi = (commandSchema: S.t<unknown>): bool =>
|
|
|
5
5
|
|
|
6
6
|
// Variants excluded by a mark on the member itself. This is the half that
|
|
7
7
|
// survives a spread: splicing copies members, not the metadata of the union they
|
|
8
|
-
// came from, so a command a trait marked internal is only still
|
|
8
|
+
// came from, so a command a trait marked internal is only still recognizable here.
|
|
9
9
|
let excludedByMembers = (commandSchema: S.t<unknown>): array<string> =>
|
|
10
10
|
switch commandSchema {
|
|
11
11
|
| AnyOf({anyOf}) =>
|
|
@@ -96,7 +96,7 @@ let rec fromSchemaType = (
|
|
|
96
96
|
// names come from the schema — see `Reventless.TaggedUnion` — so the same union
|
|
97
97
|
// is the same type wherever it appears, which is what lets AppSync merge each
|
|
98
98
|
// source API's copy back into one, and what lets the write path stamp a
|
|
99
|
-
// `__typename` the SDL will
|
|
99
|
+
// `__typename` the SDL will recognize.
|
|
100
100
|
| TaggedUnion(name, arms) =>
|
|
101
101
|
if asInput {
|
|
102
102
|
// GraphQL has no input unions. A command that wants one takes its arms as
|
|
@@ -421,7 +421,7 @@ let trailShape = (
|
|
|
421
421
|
let shape = ArrayOf(ObjectRef(entryName, fields))
|
|
422
422
|
// Walking the entries by shape skips the semantic dispatch `fromSury` does,
|
|
423
423
|
// so the marker is re-applied here. Without it the trail reaches a consumer
|
|
424
|
-
// as an unremarkable array of objects, and
|
|
424
|
+
// as an unremarkable array of objects, and recognizing it would come down to
|
|
425
425
|
// the field's name.
|
|
426
426
|
switch Reventless.Semantic.get(schema) {
|
|
427
427
|
| Some(sem) => Semantic(sem, shape)
|
|
@@ -392,7 +392,7 @@ and objectRefToJsonSchema = (
|
|
|
392
392
|
// not declare — which is precisely the mismatch that made read-model queries
|
|
393
393
|
// reference fields the server rejects.
|
|
394
394
|
//
|
|
395
|
-
// This is the single point for every JSON-Schema consumer: `
|
|
395
|
+
// This is the single point for every JSON-Schema consumer: `readSideDef.schema`,
|
|
396
396
|
// the MCP generator and the manifest all derive through this walk.
|
|
397
397
|
let internal = annotations->Option.flatMap(spec => spec.internal)->Option.getOr([])
|
|
398
398
|
fields
|
|
@@ -27,7 +27,7 @@ external asEvent: 'a => event = "%identity"
|
|
|
27
27
|
let metaInfo = event => (event->asEvent).meta->Option.map(({info}) => info)
|
|
28
28
|
|
|
29
29
|
type commandGenerator = payload => Effect.t<CommandTopic.commandOutcome, unit, unit>
|
|
30
|
-
type effectEventHandler<'context> = Runtime.
|
|
30
|
+
type effectEventHandler<'context> = Runtime.effectInvocationHandler<
|
|
31
31
|
payload,
|
|
32
32
|
'context,
|
|
33
33
|
CommandTopic.commandOutcome,
|
|
@@ -15,7 +15,12 @@ module type Resolvers = {
|
|
|
15
15
|
type runtimeParts
|
|
16
16
|
|
|
17
17
|
let handleResolversEvent: CommandGenerator.commandGenerator => Pulumi.Output.t<
|
|
18
|
-
Runtime.
|
|
18
|
+
Runtime.effectInvocationHandler<
|
|
19
|
+
CommandGenerator.payload,
|
|
20
|
+
'context,
|
|
21
|
+
CommandTopic.commandOutcome,
|
|
22
|
+
unit,
|
|
23
|
+
>,
|
|
19
24
|
>
|
|
20
25
|
|
|
21
26
|
let make: resolversMaker<api, runtimeParts>
|
|
@@ -48,12 +48,12 @@ module type T = {
|
|
|
48
48
|
let makeHandler: (
|
|
49
49
|
~commandTopic: component,
|
|
50
50
|
~commandsHandler: commandsHandler,
|
|
51
|
-
) => Pulumi.Output.t<Runtime.
|
|
51
|
+
) => Pulumi.Output.t<Runtime.effectInvocationHandler<callbackEvent, 'context, unit, string>>
|
|
52
52
|
|
|
53
53
|
// Returns the filtering handler output for runtime connection
|
|
54
54
|
// Registers the handler with the channel's event routing
|
|
55
55
|
let makeFilteringHandler: component => Pulumi.Output.t<
|
|
56
|
-
Runtime.
|
|
56
|
+
Runtime.effectInvocationHandler<callbackEvent, 'context, unit, string>,
|
|
57
57
|
>
|
|
58
58
|
|
|
59
59
|
let make: (
|
|
@@ -12,7 +12,7 @@ and channel<'callbackEvent, 'context, 'channelParts, 'runtimeParts> = {
|
|
|
12
12
|
publishJsonsStream: Pulumi.Output.t<ReventlessInfra.CommandTopic.publishJsonsStream>,
|
|
13
13
|
publishJsonsAndWait: Pulumi.Output.t<option<CommandTopic.publishJsonsAndWait>>,
|
|
14
14
|
handleChannelEvent: CommandTopic.jsonCommandsHandler => Pulumi.Output.t<
|
|
15
|
-
Runtime.
|
|
15
|
+
Runtime.effectInvocationHandler<'callbackEvent, 'context, unit, string>,
|
|
16
16
|
>,
|
|
17
17
|
connect: connect<'callbackEvent, 'context, 'channelParts, 'runtimeParts>,
|
|
18
18
|
}
|
|
@@ -12,7 +12,7 @@ during inline `publishJsonsAndWait` execution and read by `runInlineAndCollect`
|
|
|
12
12
|
type acceptedResult = {entityId?: string, eventCount: int}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
Carries an encoded domain rejection from `
|
|
15
|
+
Carries an encoded domain rejection from `Decision.decide` back to a synchronous producer.
|
|
16
16
|
`errorCode` is the variant tag of `Spec.errorSchema` (e.g. `"AlreadyExists"`); `errorDetail`
|
|
17
17
|
is the JSON-stringified error payload (or empty string for payload-less variants).
|
|
18
18
|
*/
|
|
@@ -21,7 +21,7 @@ type rejectedResult = {errorCode: string, errorDetail: string}
|
|
|
21
21
|
/**
|
|
22
22
|
Why a command was refused.
|
|
23
23
|
|
|
24
|
-
`DomainRejection` is `
|
|
24
|
+
`DomainRejection` is `Decision.decide` returning `Error` — the model declining the command,
|
|
25
25
|
which is the model working. `InfrastructureFailure` is the decision never landing: an event-log
|
|
26
26
|
append that failed outright or exhausted its conflict retries. Both arrive on `reportRejected`,
|
|
27
27
|
so without this a consumer would read a broken event log as a business rejection.
|
|
@@ -92,7 +92,7 @@ let fireCommandOutcome = (~component: string, ~reference: string, outcome: obser
|
|
|
92
92
|
let acceptedResultChannel: ref<option<(string, acceptedResult) => unit>> = ref(None)
|
|
93
93
|
|
|
94
94
|
// Sibling side-channel for domain rejections — populated by callbacks when
|
|
95
|
-
// `
|
|
95
|
+
// `Decision.decide` returns `Error`. Read by runInlineAndCollect to build
|
|
96
96
|
// `Rejected` outcomes that carry the real error code and detail.
|
|
97
97
|
let rejectedResultChannel: ref<option<(string, rejectedResult) => unit>> = ref(None)
|
|
98
98
|
|
|
@@ -234,7 +234,7 @@ let runInlineAndCollect = async (
|
|
|
234
234
|
rejectedResultChannel.contents = None
|
|
235
235
|
jsons->Array.mapWithIndex((cmdJson, i) => {
|
|
236
236
|
let msgId = cmdJson.meta.msgId
|
|
237
|
-
// Domain rejection from
|
|
237
|
+
// Domain rejection from Decision.decide takes precedence over both Accepted
|
|
238
238
|
// and the synthesized "Conflict" — it carries the most informative error.
|
|
239
239
|
switch rejectedResults->Dict.get(msgId) {
|
|
240
240
|
| Some({errorCode, errorDetail}) =>
|
|
@@ -24,7 +24,7 @@ module type T = {
|
|
|
24
24
|
let makeHandler: (
|
|
25
25
|
~eventCollector: component,
|
|
26
26
|
~jsonEventsHandler: jsonEventsHandler,
|
|
27
|
-
) => Pulumi.Output.t<Runtime.
|
|
27
|
+
) => Pulumi.Output.t<Runtime.effectInvocationHandler<callbackEvent, 'context, unit, string>>
|
|
28
28
|
|
|
29
29
|
let make: (
|
|
30
30
|
~name: string,
|
|
@@ -3,7 +3,7 @@ type channel<'callbackEvent, 'context, 'channelParts> = {
|
|
|
3
3
|
resources: array<ReventlessInfra.Adapter.resource>,
|
|
4
4
|
enqueueEvent: Pulumi.Output.t<EventCollector.enqueueEvent>,
|
|
5
5
|
handleChannelEvent: EventCollector.jsonEventsHandler => Pulumi.Output.t<
|
|
6
|
-
Runtime.
|
|
6
|
+
Runtime.effectInvocationHandler<'callbackEvent, 'context, unit, string>,
|
|
7
7
|
>,
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -17,8 +17,8 @@ module type T = {
|
|
|
17
17
|
|
|
18
18
|
// Retry schedule for transient storage errors.
|
|
19
19
|
// Exponential backoff: 100ms, ~200ms, ~400ms, ~800ms, ~1600ms — max 5 retries.
|
|
20
|
-
// Only retries on
|
|
21
|
-
// Retry only
|
|
20
|
+
// Only retries on recognized transient error messages; permanent errors propagate immediately.
|
|
21
|
+
// Retry only recognized transient storage failures — never a Conflict (a retry
|
|
22
22
|
// of the same append would just conflict again; the OCC retry happens a layer up
|
|
23
23
|
// via replay+re-decide).
|
|
24
24
|
let isTransient = (e: EventLog.appendError) =>
|
|
@@ -19,7 +19,7 @@ module type T = {
|
|
|
19
19
|
~id: string,
|
|
20
20
|
~timeout: int,
|
|
21
21
|
~publishToPluginExtensionPoint: CommandTopic.publishJsons,
|
|
22
|
-
) => Pulumi.Output.t<Runtime.
|
|
22
|
+
) => Pulumi.Output.t<Runtime.invocationHandler<unit, 'context, unit>>
|
|
23
23
|
|
|
24
24
|
let make: (~name: string, ~opts: Pulumi.ComponentResource.options=?) => component
|
|
25
25
|
}
|
|
@@ -23,7 +23,7 @@ module type T = {
|
|
|
23
23
|
/** Bare event-variant names consumed across every projection mapping's source event
|
|
24
24
|
schema — names the actual events (so a DCB-log-sourced mapping is captured), unlike
|
|
25
25
|
`sourceNames` which only lists source aggregate / DCB-log names. Surfaced by
|
|
26
|
-
Plugin_Structure as `
|
|
26
|
+
Plugin_Structure as `readSideDef.consumedEventTypes`. */
|
|
27
27
|
let consumedEventNames: array<string>
|
|
28
28
|
let outputs: component => outputs
|
|
29
29
|
let operations: component => Pulumi.Output.t<operations>
|
|
@@ -17,7 +17,7 @@ let toResolvedOutputs = (outputs: outputs): Pulumi.Output.t<
|
|
|
17
17
|
|
|
18
18
|
module type T = {
|
|
19
19
|
module Spec: Reventless.StateChangeSlice.Spec
|
|
20
|
-
module
|
|
20
|
+
module Decision: Reventless.StateChangeSlice.Decision with module Spec := Spec
|
|
21
21
|
/** `true` when built with `Platform.StateChangeSlice.MakeAsync` — uses FIFO channel, returns `CommandPending`. */
|
|
22
22
|
let isAsync: bool
|
|
23
23
|
type component = Component.t<t, outputs, operations>
|
|
@@ -2,13 +2,13 @@ let log = Logger.fromEnv()
|
|
|
2
2
|
|
|
3
3
|
module Make = (
|
|
4
4
|
Spec: Reventless.StateChangeSlice.Spec,
|
|
5
|
-
|
|
5
|
+
Decision: Reventless.StateChangeSlice.Decision with module Spec := Spec,
|
|
6
6
|
): StateChangeSlice.T => {
|
|
7
7
|
module Spec = Spec
|
|
8
|
-
module
|
|
8
|
+
module Decision = Decision
|
|
9
9
|
let isAsync = false
|
|
10
10
|
type component = StateChangeSlice.component
|
|
11
|
-
module Callback = StateChangeSlice_Callback.Make(Spec,
|
|
11
|
+
module Callback = StateChangeSlice_Callback.Make(Spec, Decision)
|
|
12
12
|
|
|
13
13
|
let makeJsonHandler = (
|
|
14
14
|
~tagKeysByEventType,
|