@reventlessdev/reventless-core 3.0.0-alpha.181 → 3.0.0-alpha.183

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 CHANGED
@@ -3,6 +3,21 @@
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.183 (2026-07-27)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **aws:** keep InboundTranslationSlice runtime path out of the Pulumi graph ([9166cff](https://github.com/ReventlessDev/reventless-core/commit/9166cff85939012d12e44835e2eb79a88fd1b66a))
11
+
12
+
13
+ # 3.0.0-alpha.182 (2026-07-26)
14
+
15
+ ### Bug Fixes
16
+
17
+ * **api:** commit regenerated SchemaType output for nullable optional enums ([6d48f53](https://github.com/ReventlessDev/reventless-core/commit/6d48f53c31489b8cb312c4bc63dac3b927145bd4))
18
+ * **api:** preserve nullability for optional enum fields in generated GraphQL ([3110a4a](https://github.com/ReventlessDev/reventless-core/commit/3110a4af032440ba2639519f61f221960ac78fba))
19
+
20
+
6
21
  # 3.0.0-alpha.181 (2026-07-26)
7
22
 
8
23
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.181",
3
+ "version": "3.0.0-alpha.183",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -28,16 +28,16 @@
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.31",
31
32
  "@reventlessdev/rescript-fast-csv": "1.2.0-alpha.14",
32
- "@reventlessdev/rescript-hash-object": "1.2.0-alpha.13",
33
33
  "@reventlessdev/rescript-jest": "1.0.0-alpha.9",
34
34
  "@reventlessdev/rescript-node-streams": "1.1.0-alpha.13",
35
- "@reventlessdev/rescript-effect": "0.1.0-alpha.31",
35
+ "@reventlessdev/rescript-hash-object": "1.2.0-alpha.13",
36
36
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
37
- "@reventlessdev/rescript-ssh2": "1.1.0-alpha.14",
38
37
  "@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
39
- "@reventlessdev/reventless-infra": "3.0.0-alpha.105",
40
38
  "@reventlessdev/reventless-interop": "3.0.0-alpha.29",
39
+ "@reventlessdev/rescript-ssh2": "1.1.0-alpha.14",
40
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.105",
41
41
  "@reventlessdev/reventless-spec": "3.0.0-alpha.81"
42
42
  },
43
43
  "devDependencies": {
@@ -82,6 +82,7 @@ let rec fromSury = (~parentName: string, ~fieldName: string, schema: S.t<unknown
82
82
  | _ => true
83
83
  }
84
84
  )
85
+ let isOptional = nonNullVariants->Array.length < anyOf->Array.length
85
86
  if nonNullVariants->Array.length == 1 {
86
87
  Nullable(
87
88
  fromSury(
@@ -102,7 +103,8 @@ let rec fromSury = (~parentName: string, ~fieldName: string, schema: S.t<unknown
102
103
  parentName ++
103
104
  fieldName->String.charAt(0)->String.toUpperCase ++
104
105
  fieldName->String.slice(~start=1, ~end=fieldName->String.length)
105
- Enum(enumName, constValues)
106
+ let enum = Enum(enumName, constValues)
107
+ isOptional ? Nullable(enum) : enum
106
108
  } else {
107
109
  Unknown
108
110
  }
@@ -83,7 +83,8 @@ function fromSury(parentName, fieldName, schema) {
83
83
  };
84
84
  }
85
85
  case "union" :
86
- let nonNullVariants = schema.anyOf.filter(v => {
86
+ let anyOf = schema.anyOf;
87
+ let nonNullVariants = anyOf.filter(v => {
87
88
  switch (v.type) {
88
89
  case "null" :
89
90
  case "undefined" :
@@ -92,6 +93,7 @@ function fromSury(parentName, fieldName, schema) {
92
93
  return true;
93
94
  }
94
95
  });
96
+ let isOptional = nonNullVariants.length < anyOf.length;
95
97
  if (nonNullVariants.length === 1) {
96
98
  return {
97
99
  TAG: "Nullable",
@@ -111,11 +113,19 @@ function fromSury(parentName, fieldName, schema) {
111
113
  return "Unknown";
112
114
  }
113
115
  let enumName = parentName + fieldName.charAt(0).toUpperCase() + fieldName.slice(1, fieldName.length);
114
- return {
116
+ let $$enum = {
115
117
  TAG: "Enum",
116
118
  _0: enumName,
117
119
  _1: constValues
118
120
  };
121
+ if (isOptional) {
122
+ return {
123
+ TAG: "Nullable",
124
+ _0: $$enum
125
+ };
126
+ } else {
127
+ return $$enum;
128
+ }
119
129
  default:
120
130
  return "Unknown";
121
131
  }
@@ -720,7 +720,7 @@ module Make = (
720
720
  let result = await receiveFn(args)
721
721
  let response =
722
722
  result
723
- ->InboundTranslationSlice.receiveResultToOutcome
723
+ ->InboundTranslationSlice_Callback.receiveResultToOutcome
724
724
  ->CommandTopic.commandOutcomeToJson
725
725
  response->Obj.magic
726
726
  })
@@ -23,7 +23,6 @@ import * as ApiNoApiHelpers$ReventlessCore from "../Api/ApiNoApiHelpers.res.mjs"
23
23
  import * as Plugin_Structure$ReventlessCore from "../../plugin/component/Plugin_Structure.res.mjs";
24
24
  import * as DcbEventLog_Builder$ReventlessCore from "../DcbEventLog/DcbEventLog_Builder.res.mjs";
25
25
  import * as CommandTopic_Builder$ReventlessCore from "../CommandTopic/CommandTopic_Builder.res.mjs";
26
- import * as InboundTranslationSlice$ReventlessCore from "../InboundTranslationSlice/InboundTranslationSlice.res.mjs";
27
26
  import * as AutomationSlice_Callback$ReventlessCore from "../AutomationSlice/AutomationSlice_Callback.res.mjs";
28
27
  import * as CommandGenerator_Callback$ReventlessCore from "../CommandGenerator/CommandGenerator_Callback.res.mjs";
29
28
  import * as InboundTranslationSlice_Callback$ReventlessCore from "../InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs";
@@ -384,7 +383,7 @@ function Make(DcbEventLogStorage) {
384
383
  if (receiveFn !== undefined) {
385
384
  return Effect.promise(async () => {
386
385
  let result = await receiveFn(args);
387
- return CommandTopic$ReventlessCore.commandOutcomeToJson(InboundTranslationSlice$ReventlessCore.receiveResultToOutcome(result));
386
+ return CommandTopic$ReventlessCore.commandOutcomeToJson(InboundTranslationSlice_Callback$ReventlessCore.receiveResultToOutcome(result));
388
387
  });
389
388
  } else {
390
389
  return baseHandler(event, ctx);
@@ -5,31 +5,8 @@ type outputs = ReventlessInfra.InboundTranslationSlice.outputs
5
5
  type operations = ReventlessInfra.InboundTranslationSlice.operations
6
6
  type acceptedResult = ReventlessInfra.InboundTranslationSlice.acceptedResult
7
7
  type rejectedResult = ReventlessInfra.InboundTranslationSlice.rejectedResult
8
- type receiveResult = ReventlessInfra.InboundTranslationSlice.receiveResult
9
8
  type component = Component.t<t, outputs, operations>
10
9
 
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
-
33
10
  let toResolvedOutputs = (
34
11
  outputs: outputs,
35
12
  ): Pulumi.Output.t<ReventlessInterop.InboundTranslationSlice.resolvedOutputs> =>
@@ -3,36 +3,6 @@
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
-
36
6
  function toResolvedOutputs(outputs) {
37
7
  return Pulumi.all([
38
8
  Adapter$ReventlessCore.resourcesToInterop(outputs.resources),
@@ -49,7 +19,6 @@ let componentType = "InboundTranslationSlice";
49
19
 
50
20
  export {
51
21
  componentType,
52
- receiveResultToOutcome,
53
22
  toResolvedOutputs,
54
23
  }
55
24
  /* @pulumi/pulumi Not a pure module */
@@ -18,6 +18,34 @@ type auditRow = {
18
18
  receivedAt: string,
19
19
  }
20
20
 
21
+ /**
22
+ Map a `receive` outcome onto the `CommandResult` union the slice's mutation field
23
+ declares, so both surfaces feed `CommandTopic.commandOutcomeToJson` rather than
24
+ hand-rolling a second `__typename` writer.
25
+
26
+ A translation can fan out across several targets; the mutation response reports
27
+ the first target as `entityId` and the fan-out count as `eventCount`. The
28
+ per-target detail stays queryable through the slice's audit read model. When the
29
+ translation legitimately produced no command, `entityId` is omitted (encoding as
30
+ `null`) rather than reporting a target that does not exist.
31
+
32
+ Lives here rather than in `InboundTranslationSlice.res` because the deployed DCB
33
+ command Lambda calls it at runtime, and the base module imports `@pulumi/pulumi`
34
+ at deploy time — keeping this runtime-pure keeps Pulumi out of the Lambda graph.
35
+ */
36
+ let receiveResultToOutcome = (
37
+ result: ReventlessInfra.InboundTranslationSlice.receiveResult,
38
+ ): CommandTopic.commandOutcome =>
39
+ switch result {
40
+ | Ok({requestId, targetIds, commandCount}) =>
41
+ switch targetIds->Array.get(0) {
42
+ | Some(entityId) => Accepted({msgId: requestId, entityId, eventCount: commandCount})
43
+ | None => Accepted({msgId: requestId, eventCount: commandCount})
44
+ }
45
+ | Error({requestId, error}) =>
46
+ Rejected({msgId: requestId, errorCode: "TranslationFailed", errorDetail: Some(error)})
47
+ }
48
+
21
49
  module type T = {
22
50
  module Spec: Reventless.InboundTranslationSlice.Spec
23
51
  module Translation: Reventless.InboundTranslationSlice.Translation with module Spec := Spec
@@ -25,6 +25,36 @@ let auditRowSchema = S.schema(s => ({
25
25
  receivedAt: s.m(S.string)
26
26
  }));
27
27
 
28
+ function receiveResultToOutcome(result) {
29
+ if (result.TAG === "Ok") {
30
+ let match = result._0;
31
+ let commandCount = match.commandCount;
32
+ let requestId = match.requestId;
33
+ let entityId = match.targetIds[0];
34
+ if (entityId !== undefined) {
35
+ return {
36
+ TAG: "Accepted",
37
+ msgId: requestId,
38
+ entityId: entityId,
39
+ eventCount: commandCount
40
+ };
41
+ } else {
42
+ return {
43
+ TAG: "Accepted",
44
+ msgId: requestId,
45
+ eventCount: commandCount
46
+ };
47
+ }
48
+ }
49
+ let match$1 = result._0;
50
+ return {
51
+ TAG: "Rejected",
52
+ msgId: match$1.requestId,
53
+ errorCode: "TranslationFailed",
54
+ errorDetail: match$1.error
55
+ };
56
+ }
57
+
28
58
  function Make(Spec) {
29
59
  return Translation => {
30
60
  let auditLog = {};
@@ -190,6 +220,7 @@ function Make(Spec) {
190
220
  export {
191
221
  auditStatusSchema,
192
222
  auditRowSchema,
223
+ receiveResultToOutcome,
193
224
  Make,
194
225
  }
195
226
  /* Not a pure module */