@reventlessdev/reventless-core 3.0.0-alpha.180 → 3.0.0-alpha.181
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 +14 -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 +3 -0
- package/src/components/Api/SchemaType.res.mjs +10 -2
- 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,20 @@
|
|
|
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.181 (2026-07-26)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **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)
|
|
11
|
+
* **core:** decode the event envelope in OutboundTranslationSlice ([66008a4](https://github.com/ReventlessDev/reventless-core/commit/66008a4dff411c85470f60aac5424e7b3eda6f01))
|
|
12
|
+
* **graphql:** give InboundTranslationSlice mutations a resolvable result type ([381b545](https://github.com/ReventlessDev/reventless-core/commit/381b5458c16797404cb8ed95fa853fb1e1ca4199))
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **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)
|
|
16
|
+
* **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)
|
|
17
|
+
* **dcb:** emit decision-model cache hit/miss + delta metrics ([967fb7f](https://github.com/ReventlessDev/reventless-core/commit/967fb7fb42f00d22b9dd6f6261e2f5308a6e9516))
|
|
18
|
+
|
|
19
|
+
|
|
6
20
|
# 3.0.0-alpha.180 (2026-07-24)
|
|
7
21
|
|
|
8
22
|
### 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.181",
|
|
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.30",
|
|
32
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.9",
|
|
33
31
|
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.14",
|
|
34
32
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.13",
|
|
33
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.9",
|
|
35
34
|
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.13",
|
|
35
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.31",
|
|
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",
|
|
39
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.105",
|
|
38
40
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.29",
|
|
39
|
-
"@reventlessdev/reventless-
|
|
40
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.80",
|
|
41
|
-
"@reventlessdev/rescript-uuid": "1.1.0-alpha.17"
|
|
41
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.81"
|
|
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
|
|
@@ -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";
|
|
@@ -133,8 +138,11 @@ function fromSuryObject(typeName, schema) {
|
|
|
133
138
|
|
|
134
139
|
let isTagged = DcbTag$Reventless.isTagged;
|
|
135
140
|
|
|
141
|
+
let isDateTime = DateTime$Reventless.isDateTime;
|
|
142
|
+
|
|
136
143
|
export {
|
|
137
144
|
isTagged,
|
|
145
|
+
isDateTime,
|
|
138
146
|
isIdFieldName,
|
|
139
147
|
isIdsFieldName,
|
|
140
148
|
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 */
|
|
@@ -29,7 +29,7 @@ module type T = {
|
|
|
29
29
|
let receive: (
|
|
30
30
|
ReventlessInfra.CommandTopic.publishJsons,
|
|
31
31
|
JSON.t,
|
|
32
|
-
) => promise<
|
|
32
|
+
) => promise<ReventlessInfra.InboundTranslationSlice.receiveResult>
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
module Make = (
|
|
@@ -53,7 +53,7 @@ module Make = (
|
|
|
53
53
|
let receive = async (
|
|
54
54
|
publishJsons: ReventlessInfra.CommandTopic.publishJsons,
|
|
55
55
|
inputJson: JSON.t,
|
|
56
|
-
) => {
|
|
56
|
+
): ReventlessInfra.InboundTranslationSlice.receiveResult => {
|
|
57
57
|
let requestId = Uuid.v4()
|
|
58
58
|
|
|
59
59
|
// Parse the external input
|
|
@@ -78,7 +78,7 @@ module Make = (
|
|
|
78
78
|
receivedAt: now(),
|
|
79
79
|
},
|
|
80
80
|
)
|
|
81
|
-
Error(msg)
|
|
81
|
+
Error({requestId, error: msg})
|
|
82
82
|
|
|
83
83
|
| Ok(input) =>
|
|
84
84
|
switch Translation.translate(input) {
|
|
@@ -94,7 +94,7 @@ module Make = (
|
|
|
94
94
|
receivedAt: now(),
|
|
95
95
|
},
|
|
96
96
|
)
|
|
97
|
-
Ok([])
|
|
97
|
+
Ok({requestId, targetIds: [], commandCount: 0})
|
|
98
98
|
} else {
|
|
99
99
|
// Encode all commands; abort on first encoding failure
|
|
100
100
|
let msgs = ref([])
|
|
@@ -134,7 +134,7 @@ module Make = (
|
|
|
134
134
|
receivedAt: now(),
|
|
135
135
|
},
|
|
136
136
|
)
|
|
137
|
-
Error(msg)
|
|
137
|
+
Error({requestId, error: msg})
|
|
138
138
|
| None =>
|
|
139
139
|
try {
|
|
140
140
|
await publishJsons(msgs.contents)
|
|
@@ -152,7 +152,7 @@ module Make = (
|
|
|
152
152
|
receivedAt: now(),
|
|
153
153
|
},
|
|
154
154
|
)
|
|
155
|
-
Ok(targetIds)
|
|
155
|
+
Ok({requestId, targetIds, commandCount: pairs->Array.length})
|
|
156
156
|
} catch {
|
|
157
157
|
| exn =>
|
|
158
158
|
let msg =
|
|
@@ -169,7 +169,7 @@ module Make = (
|
|
|
169
169
|
receivedAt: now(),
|
|
170
170
|
},
|
|
171
171
|
)
|
|
172
|
-
Error(msg)
|
|
172
|
+
Error({requestId, error: msg})
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
}
|
|
@@ -184,7 +184,7 @@ module Make = (
|
|
|
184
184
|
receivedAt: now(),
|
|
185
185
|
},
|
|
186
186
|
)
|
|
187
|
-
Error(msg)
|
|
187
|
+
Error({requestId, error: msg})
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
}
|