@reventlessdev/reventless-core 3.0.0-alpha.180 → 3.0.0-alpha.182
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 +22 -0
- package/package.json +7 -7
- package/src/Message.res +2 -1
- package/src/Message.res.mjs +5 -1
- package/src/admin/Platform_Admin_Structure.res +2 -0
- package/src/admin/Platform_Admin_Structure.res.mjs +2 -0
- package/src/admin/Platform_ComponentDefinitionsApi.res +2 -1
- package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +5 -1
- package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res +1 -1
- package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res.mjs +2 -1
- package/src/components/Api/ApiTargetStateHelpers.res +20 -0
- package/src/components/Api/ApiTargetStateHelpers.res.mjs +16 -0
- package/src/components/Api/GraphQL_FragmentGenerator.res +1 -0
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +1 -0
- package/src/components/Api/SchemaType.res +6 -1
- package/src/components/Api/SchemaType.res.mjs +22 -4
- package/src/components/Api/SuryToJsonSchema.res +16 -0
- package/src/components/Api/SuryToJsonSchema.res.mjs +28 -0
- package/src/components/Dcb/Dcb_Builder.res +4 -5
- package/src/components/Dcb/Dcb_Builder.res.mjs +3 -5
- package/src/components/DcbEventLog/DcbEventLog_Operations.res +6 -1
- package/src/components/DcbEventLog/DcbEventLog_Operations.res.mjs +2 -1
- package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +25 -0
- package/src/components/InboundTranslationSlice/InboundTranslationSlice.res.mjs +31 -0
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +8 -8
- package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +26 -6
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +44 -37
- package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res.mjs +17 -25
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +27 -2
- package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +4 -0
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +20 -6
- package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +23 -5
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +1 -1
- package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +5 -1
- package/src/plugin/component/Plugin_Helpers.res +4 -1
- package/src/plugin/component/Plugin_Structure.res +15 -1
- package/src/plugin/component/Plugin_Structure.res.mjs +4 -1
- package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +2 -0
- package/tests/IdentityTest.res.mjs +174 -0
- package/tests/ResourceAttributionTest.res.mjs +75 -0
- package/tests/adapter/MonitoringTest.res.mjs +116 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res +10 -0
- package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +365 -0
- package/tests/admin/Platform_UIFragmentsApiTest.res.mjs +101 -0
- package/tests/aggregate/AggregateCacheTest.res.mjs +433 -0
- package/tests/aggregate/AggregateCallbackTest.res.mjs +143 -0
- package/tests/aggregate/AggregateCausationTest.res.mjs +47 -0
- package/tests/aggregate/AggregateConflictTest.res.mjs +386 -0
- package/tests/aggregate/AggregateFixtures.res.mjs +308 -0
- package/tests/aggregate/AggregateRejectionTest.res.mjs +166 -0
- package/tests/aggregate/AggregateSnapshotTest.res.mjs +645 -0
- package/tests/api/ApiNoApiTest.res.mjs +90 -0
- package/tests/api/GraphQL_StitcherTest.res.mjs +112 -0
- package/tests/api/SimpleTest.res.mjs +10 -0
- package/tests/api/SuryToJsonSchemaTest.res +68 -0
- package/tests/api/SuryToJsonSchemaTest.res.mjs +843 -0
- package/tests/commandgenerator/CommandGeneratorCallbackTest.res.mjs +160 -0
- package/tests/commandgenerator/CommandGeneratorFixtures.res.mjs +197 -0
- package/tests/commandtopic/CommandTopicCallbackFixtures.res.mjs +152 -0
- package/tests/commandtopic/CommandTopicCallbackTest.res.mjs +106 -0
- package/tests/commandtopic/CommandTopicHelpersRejectionTest.res.mjs +109 -0
- package/tests/counter/CounterCallbackTest.res.mjs +140 -0
- package/tests/counter/CounterFixtures.res.mjs +67 -0
- package/tests/dcb/DcbDecodeTest.res.mjs +194 -0
- package/tests/dcb/DcbEventLogOperationsTest.res.mjs +268 -0
- package/tests/dcb/DcbFixtures.res.mjs +534 -0
- package/tests/dcb/DcbScopeInferenceTest.res.mjs +480 -0
- package/tests/dcb/DcbStateChangeSliceTest.res.mjs +540 -0
- package/tests/dcb/DcbTagTest.res.mjs +867 -0
- package/tests/dcb/DcbValidationTest.res.mjs +476 -0
- package/tests/eventlog/EventLogFixtures.res.mjs +196 -0
- package/tests/eventlog/EventLogOperationsTest.res.mjs +192 -0
- package/tests/eventlog/EventLogRetryTest.res.mjs +195 -0
- package/tests/eventmapper/EventMapperCallbackTest.res.mjs +141 -0
- package/tests/eventmapper/EventMapperFixtures.res.mjs +440 -0
- package/tests/eventmapper/EventMapperTest.res.mjs +83 -0
- package/tests/eventtopic/EventTopicFixtures.res.mjs +65 -0
- package/tests/eventtopic/EventTopicOperationsTest.res.mjs +71 -0
- package/tests/extensionpoint/ExtensionPointCallbackTest.res.mjs +94 -0
- package/tests/extensionpoint/ExtensionPointFixtures.res.mjs +205 -0
- package/tests/extensionpoint/ExtensionPointOperationsTest.res.mjs +304 -0
- package/tests/extensionpoint/PluginDisconnectScheduleTest.res.mjs +92 -0
- package/tests/ftp/FTPTest.res.mjs +88 -0
- package/tests/logger/LogFormatTest.res.mjs +329 -0
- package/tests/logger/LoggerLazyTest.res.mjs +40 -0
- package/tests/message/MessageTest.res.mjs +218 -0
- package/tests/message/MetaEnvelopeTest.res.mjs +188 -0
- package/tests/plugin/HeartbeatDisconnectGraceTest.res.mjs +91 -0
- package/tests/plugin/ManifestVisibilityTest.res.mjs +215 -0
- package/tests/plugin/PluginSpecExperiment.res.mjs +31 -0
- package/tests/plugin/PluginStructureTest.res +55 -0
- package/tests/plugin/PluginStructureTest.res.mjs +467 -0
- package/tests/plugin/PluginVersionTest.res.mjs +30 -0
- package/tests/plugin/SchemaWalkerTest.res.mjs +160 -0
- package/tests/plugin/StateChangeSlice/PsPlaceOrder.res.mjs +62 -0
- package/tests/plugin/StateChangeSlice/PsShipOrder.res +4 -1
- package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +77 -0
- package/tests/plugin/StateViewSlice/PsAnnotatedView.res +5 -4
- package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +110 -0
- package/tests/plugin/StateViewSlice/PsAvailableProductsView.res +1 -1
- package/tests/plugin/StateViewSlice/PsAvailableProductsView.res.mjs +70 -0
- package/tests/plugin/StateViewSlice/PsCustomersView.res +1 -1
- package/tests/plugin/StateViewSlice/PsCustomersView.res.mjs +70 -0
- package/tests/plugin/StateViewSlice/PsOrdersView.res +1 -1
- package/tests/plugin/StateViewSlice/PsOrdersView.res.mjs +82 -0
- package/tests/poc/StructuralSubtypingPoc.res.mjs +61 -0
- package/tests/projection/ProjectionOptimizeTest.res.mjs +75 -0
- package/tests/querydb/QueryDbFixtures.res.mjs +135 -0
- package/tests/querydb/QueryDbOperationsTest.res.mjs +168 -0
- package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res.mjs +77 -0
- package/tests/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +139 -0
- package/tests/util/CommandPublisherTest.res.mjs +78 -0
- package/tests/util/CsvStreamTest.res.mjs +51 -0
- package/tests/util/LruTest.res.mjs +105 -0
- package/tests/util/MetricsTest.res.mjs +53 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.182 (2026-07-26)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **api:** commit regenerated SchemaType output for nullable optional enums ([6d48f53](https://github.com/ReventlessDev/reventless-core/commit/6d48f53c31489b8cb312c4bc63dac3b927145bd4))
|
|
11
|
+
* **api:** preserve nullability for optional enum fields in generated GraphQL ([3110a4a](https://github.com/ReventlessDev/reventless-core/commit/3110a4af032440ba2639519f61f221960ac78fba))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# 3.0.0-alpha.181 (2026-07-26)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **auto-ui:** don't hand a [@no](https://github.com/no)Api command variant a sibling's mutation field ([2640465](https://github.com/ReventlessDev/reventless-core/commit/264046509c5f5fd7f00f5d04ab977bbc2008098c)), closes [#2](https://github.com/ReventlessDev/reventless-core/issues/2)
|
|
19
|
+
* **core:** decode the event envelope in OutboundTranslationSlice ([66008a4](https://github.com/ReventlessDev/reventless-core/commit/66008a4dff411c85470f60aac5424e7b3eda6f01))
|
|
20
|
+
* **graphql:** give InboundTranslationSlice mutations a resolvable result type ([381b545](https://github.com/ReventlessDev/reventless-core/commit/381b5458c16797404cb8ed95fa853fb1e1ca4199))
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* **auto-ui:** declare command target state via [@target](https://github.com/target)State ([5fc0374](https://github.com/ReventlessDev/reventless-core/commit/5fc03741a8816c57085b86a4ad7d595e3b690193)), closes [#5](https://github.com/ReventlessDev/reventless-core/issues/5)
|
|
24
|
+
* **auto-ui:** declare field semantics + dashboard metrics ([@semantic](https://github.com/semantic), [@metric](https://github.com/metric)) ([d74ff77](https://github.com/ReventlessDev/reventless-core/commit/d74ff7721e18e8638a82931a370a549b304dac94)), closes [#4](https://github.com/ReventlessDev/reventless-core/issues/4)
|
|
25
|
+
* **dcb:** emit decision-model cache hit/miss + delta metrics ([967fb7f](https://github.com/ReventlessDev/reventless-core/commit/967fb7fb42f00d22b9dd6f6261e2f5308a6e9516))
|
|
26
|
+
|
|
27
|
+
|
|
6
28
|
# 3.0.0-alpha.180 (2026-07-24)
|
|
7
29
|
|
|
8
30
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.182",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -28,22 +28,22 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"sury": "11.0.0-alpha.4",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.
|
|
32
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.9",
|
|
31
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.31",
|
|
33
32
|
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.14",
|
|
34
33
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.13",
|
|
34
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.9",
|
|
35
35
|
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.13",
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
|
|
37
37
|
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.14",
|
|
38
|
+
"@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
|
|
38
39
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.29",
|
|
39
|
-
"@reventlessdev/reventless-
|
|
40
|
-
"@reventlessdev/reventless-
|
|
41
|
-
"@reventlessdev/rescript-uuid": "1.1.0-alpha.17"
|
|
40
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.81",
|
|
41
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.105"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"rescript": "12.3.0",
|
|
45
45
|
"sury-ppx": "11.0.0-alpha.2",
|
|
46
|
-
"@reventlessdev/reventless-ppx": "1.0.0-alpha.
|
|
46
|
+
"@reventlessdev/reventless-ppx": "1.0.0-alpha.56"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"rescript": "12.3.0"
|
package/src/Message.res
CHANGED
|
@@ -257,10 +257,11 @@ let decomposeMeta = meta =>
|
|
|
257
257
|
->Option.getOrThrow
|
|
258
258
|
->Dict.toArray
|
|
259
259
|
|
|
260
|
-
let composeEventJson' = (id, meta, eventJson) =>
|
|
260
|
+
let composeEventJson' = (id, meta, ~recordedAt, eventJson) =>
|
|
261
261
|
[
|
|
262
262
|
("id", id->JSON.Encode.string),
|
|
263
263
|
("meta", meta->S.reverseConvertToJsonOrThrow(metaSchema)),
|
|
264
|
+
("recordedAt", recordedAt->JSON.Encode.string),
|
|
264
265
|
("event", eventJson),
|
|
265
266
|
]
|
|
266
267
|
->Dict.fromArray
|
package/src/Message.res.mjs
CHANGED
|
@@ -203,7 +203,7 @@ function decomposeMeta(meta) {
|
|
|
203
203
|
return Object.entries(Stdlib_Option.getOrThrow(Stdlib_JSON.Decode.object(S.reverseConvertToJsonOrThrow(meta, Message$Reventless.metaSchema)), undefined));
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
function composeEventJson$p(id, meta, eventJson) {
|
|
206
|
+
function composeEventJson$p(id, meta, recordedAt, eventJson) {
|
|
207
207
|
return Object.fromEntries([
|
|
208
208
|
[
|
|
209
209
|
"id",
|
|
@@ -213,6 +213,10 @@ function composeEventJson$p(id, meta, eventJson) {
|
|
|
213
213
|
"meta",
|
|
214
214
|
S.reverseConvertToJsonOrThrow(meta, Message$Reventless.metaSchema)
|
|
215
215
|
],
|
|
216
|
+
[
|
|
217
|
+
"recordedAt",
|
|
218
|
+
recordedAt
|
|
219
|
+
],
|
|
216
220
|
[
|
|
217
221
|
"event",
|
|
218
222
|
eventJson
|
|
@@ -74,6 +74,7 @@ let activateCommand: commandDef = {
|
|
|
74
74
|
mutationField: Api_Naming.adminField(~name="Plugin_Activate"),
|
|
75
75
|
references: [],
|
|
76
76
|
allowedStates: Some(["Inactive"]),
|
|
77
|
+
targetState: None,
|
|
77
78
|
apiExposed: Some(true),
|
|
78
79
|
}
|
|
79
80
|
|
|
@@ -85,6 +86,7 @@ let deactivateCommand: commandDef = {
|
|
|
85
86
|
mutationField: Api_Naming.adminField(~name="Plugin_Deactivate"),
|
|
86
87
|
references: [],
|
|
87
88
|
allowedStates: Some(["Connected", "Disconnected"]),
|
|
89
|
+
targetState: None,
|
|
88
90
|
apiExposed: Some(true),
|
|
89
91
|
}
|
|
90
92
|
|
|
@@ -61,6 +61,7 @@ let activateCommand = {
|
|
|
61
61
|
mutationField: activateCommand_mutationField,
|
|
62
62
|
references: activateCommand_references,
|
|
63
63
|
allowedStates: activateCommand_allowedStates,
|
|
64
|
+
targetState: undefined,
|
|
64
65
|
apiExposed: activateCommand_apiExposed
|
|
65
66
|
};
|
|
66
67
|
|
|
@@ -87,6 +88,7 @@ let deactivateCommand = {
|
|
|
87
88
|
mutationField: deactivateCommand_mutationField,
|
|
88
89
|
references: deactivateCommand_references,
|
|
89
90
|
allowedStates: deactivateCommand_allowedStates,
|
|
91
|
+
targetState: undefined,
|
|
90
92
|
apiExposed: deactivateCommand_apiExposed
|
|
91
93
|
};
|
|
92
94
|
|
|
@@ -8,7 +8,7 @@ open Reventless.Plugin
|
|
|
8
8
|
|
|
9
9
|
let sdlTypes: array<string> = [
|
|
10
10
|
`type Platform_FieldReference {\n fieldName: String!\n entity: String!\n plugin: String\n}`,
|
|
11
|
-
`type Platform_CommandDef {\n name: String!\n schema: String!\n level: String!\n aggregateIdField: String\n mutationField: String!\n references: [Platform_FieldReference!]!\n allowedStates: [String!]\n apiExposed: Boolean\n}`,
|
|
11
|
+
`type Platform_CommandDef {\n name: String!\n schema: String!\n level: String!\n aggregateIdField: String\n mutationField: String!\n references: [Platform_FieldReference!]!\n allowedStates: [String!]\n targetState: String\n apiExposed: Boolean\n}`,
|
|
12
12
|
`type Platform_EventDef {\n name: String!\n schema: String!\n references: [Platform_FieldReference!]!\n}`,
|
|
13
13
|
`type Platform_WriteSideDef {\n name: String!\n commands: [Platform_CommandDef!]!\n linkedViews: [String!]!\n consistencyRead: String\n producedEventTypes: [String!]!\n consumedEventTypes: [String!]!\n events: [Platform_EventDef!]!\n}`,
|
|
14
14
|
`type Platform_ReadSideDef {\n name: String!\n queryField: String!\n schema: String!\n consumedEventTypes: [String!]!\n linkedWriteSide: [String!]!\n labelField: String!\n searchableFields: [String!]!\n statusField: String\n}`,
|
|
@@ -54,6 +54,7 @@ let encodeCommandDef = (c: commandDef): JSON.t =>
|
|
|
54
54
|
"allowedStates",
|
|
55
55
|
c.allowedStates->Option.mapOr(JSON.Encode.null, encodeStrings),
|
|
56
56
|
),
|
|
57
|
+
("targetState", c.targetState->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
|
|
57
58
|
("apiExposed", c.apiExposed->Option.mapOr(JSON.Encode.null, JSON.Encode.bool)),
|
|
58
59
|
])->JSON.Encode.object
|
|
59
60
|
|
|
@@ -6,7 +6,7 @@ import * as Plugin$ReventlessCore from "../plugin/component/Plugin.res.mjs";
|
|
|
6
6
|
|
|
7
7
|
let sdlTypes = [
|
|
8
8
|
`type Platform_FieldReference {\n fieldName: String!\n entity: String!\n plugin: String\n}`,
|
|
9
|
-
`type Platform_CommandDef {\n name: String!\n schema: String!\n level: String!\n aggregateIdField: String\n mutationField: String!\n references: [Platform_FieldReference!]!\n allowedStates: [String!]\n apiExposed: Boolean\n}`,
|
|
9
|
+
`type Platform_CommandDef {\n name: String!\n schema: String!\n level: String!\n aggregateIdField: String\n mutationField: String!\n references: [Platform_FieldReference!]!\n allowedStates: [String!]\n targetState: String\n apiExposed: Boolean\n}`,
|
|
10
10
|
`type Platform_EventDef {\n name: String!\n schema: String!\n references: [Platform_FieldReference!]!\n}`,
|
|
11
11
|
`type Platform_WriteSideDef {\n name: String!\n commands: [Platform_CommandDef!]!\n linkedViews: [String!]!\n consistencyRead: String\n producedEventTypes: [String!]!\n consumedEventTypes: [String!]!\n events: [Platform_EventDef!]!\n}`,
|
|
12
12
|
`type Platform_ReadSideDef {\n name: String!\n queryField: String!\n schema: String!\n consumedEventTypes: [String!]!\n linkedWriteSide: [String!]!\n labelField: String!\n searchableFields: [String!]!\n statusField: String\n}`,
|
|
@@ -71,6 +71,10 @@ function encodeCommandDef(c) {
|
|
|
71
71
|
"allowedStates",
|
|
72
72
|
Stdlib_Option.mapOr(c.allowedStates, null, encodeStrings)
|
|
73
73
|
],
|
|
74
|
+
[
|
|
75
|
+
"targetState",
|
|
76
|
+
Stdlib_Option.mapOr(c.targetState, null, prim => prim)
|
|
77
|
+
],
|
|
74
78
|
[
|
|
75
79
|
"apiExposed",
|
|
76
80
|
Stdlib_Option.mapOr(c.apiExposed, null, prim => prim)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// No `open Reventless.Plugin`: it would shadow the state labels (panels/pages/...) brought in
|
|
4
4
|
// by the projection's spec open. `manifest.<field>` resolves type-directed from the event's
|
|
5
5
|
// uiFragmentManifest payload.
|
|
6
|
-
let project = event =>
|
|
6
|
+
let project = ({event}) =>
|
|
7
7
|
switch event {
|
|
8
8
|
| UiFragmentRegistered({pluginId, manifest, at}) => [
|
|
9
9
|
Set(
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Read per-variant `targetState` metadata that the reventless-ppx
|
|
2
|
+
// `@targetState("…")` attribute attaches to a command schema. Consumed by
|
|
3
|
+
// `Plugin_Structure.toCommandDef` when building the `commandDef` records that
|
|
4
|
+
// land in `Platform_ComponentDefinitions`, so AutoUI's board drag resolver can
|
|
5
|
+
// move a row by a *declared* transition instead of a name-stem guess. Mirrors
|
|
6
|
+
// `ApiAllowedStatesHelpers.getAllowedStates`.
|
|
7
|
+
|
|
8
|
+
open ReventlessInfra.Api
|
|
9
|
+
|
|
10
|
+
/** Returns the declared target state for a given command-variant name, or None
|
|
11
|
+
when the variant has no `@targetState` annotation (back-compat default —
|
|
12
|
+
the resolver falls back to its name-stem heuristic). */
|
|
13
|
+
let getTargetState = (
|
|
14
|
+
commandSchema: S.t<unknown>,
|
|
15
|
+
~variantName: string,
|
|
16
|
+
): option<string> =>
|
|
17
|
+
switch commandSchema->S.Metadata.get(~id=targetStateId) {
|
|
18
|
+
| None => None
|
|
19
|
+
| Some(entries) => entries->Dict.get(variantName)
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Api$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Api.res.mjs";
|
|
5
|
+
|
|
6
|
+
function getTargetState(commandSchema, variantName) {
|
|
7
|
+
let entries = S.Metadata.get(commandSchema, Api$ReventlessInfra.targetStateId);
|
|
8
|
+
if (entries !== undefined) {
|
|
9
|
+
return entries[variantName];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
getTargetState,
|
|
15
|
+
}
|
|
16
|
+
/* S Not a pure module */
|
|
@@ -43,6 +43,7 @@ let rec fromSchemaType = (
|
|
|
43
43
|
| ScalarBoolean => `Boolean${bang}`
|
|
44
44
|
| ScalarBigInt => `String${bang}`
|
|
45
45
|
| EntityId => `ID${bang}`
|
|
46
|
+
| DateTime => `String${bang}`
|
|
46
47
|
| Nullable(inner) => fromSchemaType(~required=false, ~asInput, inner, collectedTypes, seenTypes)
|
|
47
48
|
| ArrayOf(item) =>
|
|
48
49
|
let itemType = fromSchemaType(~required=true, ~asInput, item, collectedTypes, seenTypes)
|
|
@@ -4,6 +4,7 @@ type rec schemaType =
|
|
|
4
4
|
| ScalarBoolean
|
|
5
5
|
| ScalarBigInt
|
|
6
6
|
| EntityId
|
|
7
|
+
| DateTime
|
|
7
8
|
| Nullable(schemaType)
|
|
8
9
|
| ArrayOf(schemaType)
|
|
9
10
|
| ObjectRef(string, dict<schemaType>)
|
|
@@ -11,6 +12,7 @@ type rec schemaType =
|
|
|
11
12
|
| Unknown
|
|
12
13
|
|
|
13
14
|
let isTagged = Reventless.DcbTag.isTagged
|
|
15
|
+
let isDateTime = Reventless.DateTime.isDateTime
|
|
14
16
|
|
|
15
17
|
let isIdFieldName = (name: string): bool => {
|
|
16
18
|
let lower = String.toLowerCase(name)
|
|
@@ -30,6 +32,7 @@ let rec fromSury = (~parentName: string, ~fieldName: string, schema: S.t<unknown
|
|
|
30
32
|
} else {
|
|
31
33
|
switch schema {
|
|
32
34
|
| String({const: ?Some(_)}) => ScalarString
|
|
35
|
+
| String(_) if isDateTime(schema) => DateTime
|
|
33
36
|
| String(_) => isIdFieldName(fieldName) ? EntityId : ScalarString
|
|
34
37
|
| Number(_) => ScalarNumber
|
|
35
38
|
| Boolean(_) => ScalarBoolean
|
|
@@ -79,6 +82,7 @@ let rec fromSury = (~parentName: string, ~fieldName: string, schema: S.t<unknown
|
|
|
79
82
|
| _ => true
|
|
80
83
|
}
|
|
81
84
|
)
|
|
85
|
+
let isOptional = nonNullVariants->Array.length < anyOf->Array.length
|
|
82
86
|
if nonNullVariants->Array.length == 1 {
|
|
83
87
|
Nullable(
|
|
84
88
|
fromSury(
|
|
@@ -99,7 +103,8 @@ let rec fromSury = (~parentName: string, ~fieldName: string, schema: S.t<unknown
|
|
|
99
103
|
parentName ++
|
|
100
104
|
fieldName->String.charAt(0)->String.toUpperCase ++
|
|
101
105
|
fieldName->String.slice(~start=1, ~end=fieldName->String.length)
|
|
102
|
-
Enum(enumName, constValues)
|
|
106
|
+
let enum = Enum(enumName, constValues)
|
|
107
|
+
isOptional ? Nullable(enum) : enum
|
|
103
108
|
} else {
|
|
104
109
|
Unknown
|
|
105
110
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
4
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
5
|
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
6
|
+
import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/types/DateTime.res.mjs";
|
|
6
7
|
import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
|
|
7
8
|
|
|
8
9
|
function isIdFieldName(name) {
|
|
@@ -31,10 +32,14 @@ function fromSury(parentName, fieldName, schema) {
|
|
|
31
32
|
}
|
|
32
33
|
switch (schema.type) {
|
|
33
34
|
case "string" :
|
|
34
|
-
if (schema.const !== undefined
|
|
35
|
+
if (schema.const !== undefined) {
|
|
35
36
|
return "ScalarString";
|
|
36
|
-
} else {
|
|
37
|
+
} else if (DateTime$Reventless.isDateTime(schema)) {
|
|
38
|
+
return "DateTime";
|
|
39
|
+
} else if (isIdFieldName(fieldName)) {
|
|
37
40
|
return "EntityId";
|
|
41
|
+
} else {
|
|
42
|
+
return "ScalarString";
|
|
38
43
|
}
|
|
39
44
|
case "number" :
|
|
40
45
|
return "ScalarNumber";
|
|
@@ -78,7 +83,8 @@ function fromSury(parentName, fieldName, schema) {
|
|
|
78
83
|
};
|
|
79
84
|
}
|
|
80
85
|
case "union" :
|
|
81
|
-
let
|
|
86
|
+
let anyOf = schema.anyOf;
|
|
87
|
+
let nonNullVariants = anyOf.filter(v => {
|
|
82
88
|
switch (v.type) {
|
|
83
89
|
case "null" :
|
|
84
90
|
case "undefined" :
|
|
@@ -87,6 +93,7 @@ function fromSury(parentName, fieldName, schema) {
|
|
|
87
93
|
return true;
|
|
88
94
|
}
|
|
89
95
|
});
|
|
96
|
+
let isOptional = nonNullVariants.length < anyOf.length;
|
|
90
97
|
if (nonNullVariants.length === 1) {
|
|
91
98
|
return {
|
|
92
99
|
TAG: "Nullable",
|
|
@@ -106,11 +113,19 @@ function fromSury(parentName, fieldName, schema) {
|
|
|
106
113
|
return "Unknown";
|
|
107
114
|
}
|
|
108
115
|
let enumName = parentName + fieldName.charAt(0).toUpperCase() + fieldName.slice(1, fieldName.length);
|
|
109
|
-
|
|
116
|
+
let $$enum = {
|
|
110
117
|
TAG: "Enum",
|
|
111
118
|
_0: enumName,
|
|
112
119
|
_1: constValues
|
|
113
120
|
};
|
|
121
|
+
if (isOptional) {
|
|
122
|
+
return {
|
|
123
|
+
TAG: "Nullable",
|
|
124
|
+
_0: $$enum
|
|
125
|
+
};
|
|
126
|
+
} else {
|
|
127
|
+
return $$enum;
|
|
128
|
+
}
|
|
114
129
|
default:
|
|
115
130
|
return "Unknown";
|
|
116
131
|
}
|
|
@@ -133,8 +148,11 @@ function fromSuryObject(typeName, schema) {
|
|
|
133
148
|
|
|
134
149
|
let isTagged = DcbTag$Reventless.isTagged;
|
|
135
150
|
|
|
151
|
+
let isDateTime = DateTime$Reventless.isDateTime;
|
|
152
|
+
|
|
136
153
|
export {
|
|
137
154
|
isTagged,
|
|
155
|
+
isDateTime,
|
|
138
156
|
isIdFieldName,
|
|
139
157
|
isIdsFieldName,
|
|
140
158
|
fromSury,
|
|
@@ -68,6 +68,21 @@ let mergeAnnotations = (
|
|
|
68
68
|
if spec.scanSort->Array.includes(fieldName) {
|
|
69
69
|
obj->Dict.set("x-reventless-scanSort", JSON.Encode.bool(true))
|
|
70
70
|
}
|
|
71
|
+
switch spec.semantic->Array.find(((field, _)) => field === fieldName) {
|
|
72
|
+
| Some((_, semanticId)) =>
|
|
73
|
+
obj->Dict.set("x-reventless-semantic", JSON.Encode.string(semanticId))
|
|
74
|
+
| None => ()
|
|
75
|
+
}
|
|
76
|
+
switch spec.metric->Array.find(((field, _)) => field === fieldName) {
|
|
77
|
+
| Some((_, m)) =>
|
|
78
|
+
// `x-reventless-metric: {aggregate, label}`. Omit an empty label so the
|
|
79
|
+
// UI derives one from the field name.
|
|
80
|
+
let entries = [("aggregate", JSON.Encode.string(m.aggregate))]
|
|
81
|
+
let entries =
|
|
82
|
+
m.label === "" ? entries : Array.concat(entries, [("label", JSON.Encode.string(m.label))])
|
|
83
|
+
obj->Dict.set("x-reventless-metric", JSON.Encode.object(Dict.fromArray(entries)))
|
|
84
|
+
| None => ()
|
|
85
|
+
}
|
|
71
86
|
JSON.Encode.object(obj)
|
|
72
87
|
}
|
|
73
88
|
|
|
@@ -80,6 +95,7 @@ let rec fromSchemaType = (st: SchemaType.schemaType): JSON.t =>
|
|
|
80
95
|
| ScalarBoolean => jsonObject([("type", str("boolean"))])
|
|
81
96
|
| ScalarBigInt => jsonObject([("type", str("integer"))])
|
|
82
97
|
| EntityId => jsonObject([("type", str("string")), ("format", str("uuid"))])
|
|
98
|
+
| DateTime => jsonObject([("type", str("string")), ("format", str("date-time"))])
|
|
83
99
|
| Nullable(inner) =>
|
|
84
100
|
let innerSchema = fromSchemaType(inner)
|
|
85
101
|
jsonObject([("oneOf", JSON.Encode.array([innerSchema, jsonObject([("type", str("null"))])]))])
|
|
@@ -62,6 +62,23 @@ function mergeAnnotations(fieldSchema, fieldName, spec) {
|
|
|
62
62
|
if (spec.scanSort.includes(fieldName)) {
|
|
63
63
|
obj["x-reventless-scanSort"] = true;
|
|
64
64
|
}
|
|
65
|
+
let match$3 = spec.semantic.find(param => param[0] === fieldName);
|
|
66
|
+
if (match$3 !== undefined) {
|
|
67
|
+
obj["x-reventless-semantic"] = match$3[1];
|
|
68
|
+
}
|
|
69
|
+
let match$4 = spec.metric.find(param => param[0] === fieldName);
|
|
70
|
+
if (match$4 !== undefined) {
|
|
71
|
+
let m = match$4[1];
|
|
72
|
+
let entries = [[
|
|
73
|
+
"aggregate",
|
|
74
|
+
m.aggregate
|
|
75
|
+
]];
|
|
76
|
+
let entries$1 = m.label === "" ? entries : entries.concat([[
|
|
77
|
+
"label",
|
|
78
|
+
m.label
|
|
79
|
+
]]);
|
|
80
|
+
obj["x-reventless-metric"] = Object.fromEntries(entries$1);
|
|
81
|
+
}
|
|
65
82
|
return obj;
|
|
66
83
|
}
|
|
67
84
|
|
|
@@ -94,6 +111,17 @@ function fromSchemaType(st) {
|
|
|
94
111
|
"uuid"
|
|
95
112
|
]
|
|
96
113
|
]);
|
|
114
|
+
case "DateTime" :
|
|
115
|
+
return Object.fromEntries([
|
|
116
|
+
[
|
|
117
|
+
"type",
|
|
118
|
+
"string"
|
|
119
|
+
],
|
|
120
|
+
[
|
|
121
|
+
"format",
|
|
122
|
+
"date-time"
|
|
123
|
+
]
|
|
124
|
+
]);
|
|
97
125
|
case "ScalarString" :
|
|
98
126
|
case "Unknown" :
|
|
99
127
|
return Object.fromEntries([[
|
|
@@ -718,11 +718,10 @@ module Make = (
|
|
|
718
718
|
| Some(receiveFn) =>
|
|
719
719
|
Effect.promise(async () => {
|
|
720
720
|
let result = await receiveFn(args)
|
|
721
|
-
let response =
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}
|
|
721
|
+
let response =
|
|
722
|
+
result
|
|
723
|
+
->InboundTranslationSlice.receiveResultToOutcome
|
|
724
|
+
->CommandTopic.commandOutcomeToJson
|
|
726
725
|
response->Obj.magic
|
|
727
726
|
})
|
|
728
727
|
| None => baseHandler(event, ctx)
|
|
@@ -15,6 +15,7 @@ import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
|
|
|
15
15
|
import * as Component$ReventlessCore from "../Component.res.mjs";
|
|
16
16
|
import * as DcbValidation$Reventless from "@reventlessdev/reventless-spec/src/components/DcbValidation.res.mjs";
|
|
17
17
|
import * as Api_Naming$ReventlessCore from "../Api/Api_Naming.res.mjs";
|
|
18
|
+
import * as CommandTopic$ReventlessCore from "../CommandTopic/CommandTopic.res.mjs";
|
|
18
19
|
import * as DcbScopeInference$Reventless from "@reventlessdev/reventless-spec/src/components/DcbScopeInference.res.mjs";
|
|
19
20
|
import * as RuntimeHints$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/RuntimeHints.res.mjs";
|
|
20
21
|
import * as Plugin_Helpers$ReventlessCore from "../../plugin/component/Plugin_Helpers.res.mjs";
|
|
@@ -22,6 +23,7 @@ import * as ApiNoApiHelpers$ReventlessCore from "../Api/ApiNoApiHelpers.res.mjs"
|
|
|
22
23
|
import * as Plugin_Structure$ReventlessCore from "../../plugin/component/Plugin_Structure.res.mjs";
|
|
23
24
|
import * as DcbEventLog_Builder$ReventlessCore from "../DcbEventLog/DcbEventLog_Builder.res.mjs";
|
|
24
25
|
import * as CommandTopic_Builder$ReventlessCore from "../CommandTopic/CommandTopic_Builder.res.mjs";
|
|
26
|
+
import * as InboundTranslationSlice$ReventlessCore from "../InboundTranslationSlice/InboundTranslationSlice.res.mjs";
|
|
25
27
|
import * as AutomationSlice_Callback$ReventlessCore from "../AutomationSlice/AutomationSlice_Callback.res.mjs";
|
|
26
28
|
import * as CommandGenerator_Callback$ReventlessCore from "../CommandGenerator/CommandGenerator_Callback.res.mjs";
|
|
27
29
|
import * as InboundTranslationSlice_Callback$ReventlessCore from "../InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs";
|
|
@@ -382,11 +384,7 @@ function Make(DcbEventLogStorage) {
|
|
|
382
384
|
if (receiveFn !== undefined) {
|
|
383
385
|
return Effect.promise(async () => {
|
|
384
386
|
let result = await receiveFn(args);
|
|
385
|
-
|
|
386
|
-
return result._0.map(prim => prim);
|
|
387
|
-
} else {
|
|
388
|
-
return result._0;
|
|
389
|
-
}
|
|
387
|
+
return CommandTopic$ReventlessCore.commandOutcomeToJson(InboundTranslationSlice$ReventlessCore.receiveResultToOutcome(result));
|
|
390
388
|
});
|
|
391
389
|
} else {
|
|
392
390
|
return baseHandler(event, ctx);
|
|
@@ -68,11 +68,16 @@ module Make = (Ops: Ops): T => {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// Storage stamped its own `recordedAt` at append but does not return it, so
|
|
72
|
+
// the topic envelope carries a publish-time stamp instead (a few ms later).
|
|
73
|
+
// StateViewSlice projections read this as `consumed.recordedAt`; the stored
|
|
74
|
+
// column remains authoritative for reads. Producer time is `meta.time`.
|
|
75
|
+
let recordedAt = Message.nowAsISOString()
|
|
71
76
|
let _ = await Array.zip(rawEvents, finalRawEventsJson)
|
|
72
77
|
->Array.map(async ((rawEvent, eventJson)) => {
|
|
73
78
|
let entityId =
|
|
74
79
|
rawEvent.tags->Array.get(0)->Option.map(t => t.value)->Option.getOr(name)
|
|
75
|
-
let eventJson' = Message.composeEventJson'(entityId, rawEvent.meta, eventJson)
|
|
80
|
+
let eventJson' = Message.composeEventJson'(entityId, rawEvent.meta, ~recordedAt, eventJson)
|
|
76
81
|
try await Ops.publishJson(serviceName, rawEvent.meta, eventJson') catch {
|
|
77
82
|
| JsExn(err) =>
|
|
78
83
|
let errMsg = err->JsExn.message->Option.getOr("unknown")
|
|
@@ -41,10 +41,11 @@ function Make(Ops) {
|
|
|
41
41
|
} else {
|
|
42
42
|
finalRawEventsJson = rawEventsJson;
|
|
43
43
|
}
|
|
44
|
+
let recordedAt = Message$ReventlessCore.nowAsISOString();
|
|
44
45
|
await Promise.all(Stdlib_Array.zip(rawEvents, finalRawEventsJson).map(async param => {
|
|
45
46
|
let rawEvent = param[0];
|
|
46
47
|
let entityId = Stdlib_Option.getOr(Stdlib_Option.map(rawEvent.tags[0], t => t.value), name);
|
|
47
|
-
let eventJson$p = Message$ReventlessCore.composeEventJson$p(entityId, rawEvent.meta, param[1]);
|
|
48
|
+
let eventJson$p = Message$ReventlessCore.composeEventJson$p(entityId, rawEvent.meta, recordedAt, param[1]);
|
|
48
49
|
try {
|
|
49
50
|
return await Ops.publishJson(serviceName, rawEvent.meta, eventJson$p);
|
|
50
51
|
} catch (raw_err) {
|
|
@@ -3,8 +3,33 @@ let componentType = ComponentType.InboundTranslationSlice
|
|
|
3
3
|
type t = ReventlessInfra.InboundTranslationSlice.t
|
|
4
4
|
type outputs = ReventlessInfra.InboundTranslationSlice.outputs
|
|
5
5
|
type operations = ReventlessInfra.InboundTranslationSlice.operations
|
|
6
|
+
type acceptedResult = ReventlessInfra.InboundTranslationSlice.acceptedResult
|
|
7
|
+
type rejectedResult = ReventlessInfra.InboundTranslationSlice.rejectedResult
|
|
8
|
+
type receiveResult = ReventlessInfra.InboundTranslationSlice.receiveResult
|
|
6
9
|
type component = Component.t<t, outputs, operations>
|
|
7
10
|
|
|
11
|
+
/**
|
|
12
|
+
Map a `receive` outcome onto the `CommandResult` union the slice's mutation field
|
|
13
|
+
declares, so both surfaces feed `CommandTopic.commandOutcomeToJson` rather than
|
|
14
|
+
hand-rolling a second `__typename` writer.
|
|
15
|
+
|
|
16
|
+
A translation can fan out across several targets; the mutation response reports
|
|
17
|
+
the first target as `entityId` and the fan-out count as `eventCount`. The
|
|
18
|
+
per-target detail stays queryable through the slice's audit read model. When the
|
|
19
|
+
translation legitimately produced no command, `entityId` is omitted (encoding as
|
|
20
|
+
`null`) rather than reporting a target that does not exist.
|
|
21
|
+
*/
|
|
22
|
+
let receiveResultToOutcome = (result: receiveResult): CommandTopic.commandOutcome =>
|
|
23
|
+
switch result {
|
|
24
|
+
| Ok({requestId, targetIds, commandCount}) =>
|
|
25
|
+
switch targetIds->Array.get(0) {
|
|
26
|
+
| Some(entityId) => Accepted({msgId: requestId, entityId, eventCount: commandCount})
|
|
27
|
+
| None => Accepted({msgId: requestId, eventCount: commandCount})
|
|
28
|
+
}
|
|
29
|
+
| Error({requestId, error}) =>
|
|
30
|
+
Rejected({msgId: requestId, errorCode: "TranslationFailed", errorDetail: Some(error)})
|
|
31
|
+
}
|
|
32
|
+
|
|
8
33
|
let toResolvedOutputs = (
|
|
9
34
|
outputs: outputs,
|
|
10
35
|
): Pulumi.Output.t<ReventlessInterop.InboundTranslationSlice.resolvedOutputs> =>
|
|
@@ -3,6 +3,36 @@
|
|
|
3
3
|
import * as Pulumi from "@pulumi/pulumi";
|
|
4
4
|
import * as Adapter$ReventlessCore from "../../adapter/Adapter.res.mjs";
|
|
5
5
|
|
|
6
|
+
function receiveResultToOutcome(result) {
|
|
7
|
+
if (result.TAG === "Ok") {
|
|
8
|
+
let match = result._0;
|
|
9
|
+
let commandCount = match.commandCount;
|
|
10
|
+
let requestId = match.requestId;
|
|
11
|
+
let entityId = match.targetIds[0];
|
|
12
|
+
if (entityId !== undefined) {
|
|
13
|
+
return {
|
|
14
|
+
TAG: "Accepted",
|
|
15
|
+
msgId: requestId,
|
|
16
|
+
entityId: entityId,
|
|
17
|
+
eventCount: commandCount
|
|
18
|
+
};
|
|
19
|
+
} else {
|
|
20
|
+
return {
|
|
21
|
+
TAG: "Accepted",
|
|
22
|
+
msgId: requestId,
|
|
23
|
+
eventCount: commandCount
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
let match$1 = result._0;
|
|
28
|
+
return {
|
|
29
|
+
TAG: "Rejected",
|
|
30
|
+
msgId: match$1.requestId,
|
|
31
|
+
errorCode: "TranslationFailed",
|
|
32
|
+
errorDetail: match$1.error
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
6
36
|
function toResolvedOutputs(outputs) {
|
|
7
37
|
return Pulumi.all([
|
|
8
38
|
Adapter$ReventlessCore.resourcesToInterop(outputs.resources),
|
|
@@ -19,6 +49,7 @@ let componentType = "InboundTranslationSlice";
|
|
|
19
49
|
|
|
20
50
|
export {
|
|
21
51
|
componentType,
|
|
52
|
+
receiveResultToOutcome,
|
|
22
53
|
toResolvedOutputs,
|
|
23
54
|
}
|
|
24
55
|
/* @pulumi/pulumi Not a pure module */
|