@reventlessdev/reventless-core 3.0.0-alpha.301 → 3.0.0-alpha.303

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/package.json +7 -7
  3. package/src/adapter/Runtime/AggregateRuntime_Builder.res +2 -2
  4. package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +10 -5
  5. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +2 -2
  6. package/src/adapter/Runtime/EventCollectorRuntime_Builder.res +1 -1
  7. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +8 -3
  8. package/src/adapter/Runtime/ExtensionPointRuntime_Builder.res +1 -1
  9. package/src/adapter/Runtime/PluginRuntime_Builder.res +3 -3
  10. package/src/adapter/Runtime/Runtime.res +10 -9
  11. package/src/adapter/Runtime/TaskRuntime_Builder.res +1 -1
  12. package/src/admin/Platform_Admin_Structure.res +3 -3
  13. package/src/admin/Platform_Admin_Structure.res.mjs +1 -1
  14. package/src/admin/Platform_BakedManifest.res +7 -7
  15. package/src/admin/Platform_BakedManifest.res.mjs +2 -2
  16. package/src/admin/Platform_ComponentDefinitionsApi.res +12 -12
  17. package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +11 -11
  18. package/src/admin/Platform_PluginStructuresApi.res +6 -6
  19. package/src/admin/Platform_PluginStructuresApi.res.mjs +4 -4
  20. package/src/admin/UiFragmentRegistry/StateView/UiFragments.res.mjs +1 -4
  21. package/src/components/Aggregate/Aggregate_Callback.res +1 -1
  22. package/src/components/Aggregate/Aggregate_Callback.res.mjs +2 -1
  23. package/src/components/Api/GraphQL_FragmentGenerator.res +9 -0
  24. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +6 -0
  25. package/src/components/Api/SuryToJsonSchema.res +1 -1
  26. package/src/components/CommandGenerator/CommandGenerator.res +1 -1
  27. package/src/components/CommandGenerator/CommandGenerator_Adapter.res +6 -1
  28. package/src/components/CommandTopic/CommandTopic.res +2 -2
  29. package/src/components/CommandTopic/CommandTopic.res.mjs +3 -0
  30. package/src/components/CommandTopic/CommandTopic_Adapter.res +1 -1
  31. package/src/components/CommandTopic/CommandTopic_Helpers.res +33 -15
  32. package/src/components/CommandTopic/CommandTopic_Helpers.res.mjs +34 -11
  33. package/src/components/EventCollector/EventCollector.res +1 -1
  34. package/src/components/EventCollector/EventCollector_Adapter.res +1 -1
  35. package/src/components/Heartbeat/Heartbeat.res +1 -1
  36. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +20 -3
  37. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +18 -4
  38. package/src/components/ReadModel/ReadModel.res +1 -1
  39. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +3 -3
  40. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +4 -3
  41. package/src/components/StateViewSlice/StateViewSlice_Builder.res +2 -2
  42. package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +1 -1
  43. package/src/components/StateViewSlice/StateViewSlice_Callback.res +2 -2
  44. package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +1 -1
  45. package/src/components/Task/Task_Adapter.res +1 -1
  46. package/src/components/Task/Task_Builder.res +1 -1
  47. package/src/plugin/component/Plugin_Builder.res +7 -7
  48. package/src/plugin/component/Plugin_Builder.res.mjs +3 -3
  49. package/src/plugin/component/Plugin_BuiltHook.res +1 -1
  50. package/src/plugin/component/Plugin_Structure.res +18 -18
  51. package/src/plugin/component/Plugin_Structure.res.mjs +8 -8
  52. package/tests/admin/AdminApiSchemaDriftTest.res +8 -8
  53. package/tests/admin/AdminApiSchemaDriftTest.res.mjs +54 -54
  54. package/tests/admin/Platform_Admin_StructureTest.res +10 -10
  55. package/tests/admin/Platform_Admin_StructureTest.res.mjs +5 -5
  56. package/tests/admin/Platform_BakedManifestTest.res +7 -7
  57. package/tests/admin/Platform_BakedManifestTest.res.mjs +9 -9
  58. package/tests/admin/Platform_ComponentDefinitionsApiTest.res +11 -11
  59. package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +5 -5
  60. package/tests/admin/Platform_PluginStructuresApiTest.res +3 -3
  61. package/tests/aggregate/AggregateRejectionTest.res +8 -1
  62. package/tests/aggregate/AggregateRejectionTest.res.mjs +9 -1
  63. package/tests/commandtopic/CommandTopicHelpersRejectionTest.res +54 -2
  64. package/tests/commandtopic/CommandTopicHelpersRejectionTest.res.mjs +51 -0
  65. package/tests/message/MessageTest.res +1 -1
  66. package/tests/plugin/ManifestVisibilityTest.res +2 -2
  67. package/tests/plugin/PluginStructureAccessTest.res.mjs +2 -2
  68. package/tests/plugin/PluginStructureTest.res +9 -9
  69. package/tests/plugin/PluginStructureTest.res.mjs +7 -7
  70. package/tests/plugin/StateView/PsAnnotatedView.res +1 -1
  71. package/tests/plugin/StateView/PsAnnotatedView.res.mjs +1 -4
  72. package/tests/plugin/StateView/PsAvailableProductsView.res.mjs +1 -4
  73. package/tests/plugin/StateView/PsCategoriesView.res.mjs +1 -4
  74. package/tests/plugin/StateView/PsCustomersView.res.mjs +1 -4
  75. package/tests/plugin/StateView/PsGatedView.res.mjs +1 -4
  76. package/tests/plugin/StateView/PsOrdersView.res.mjs +1 -4
  77. package/tests/plugin/StateView/PsShipmentsView.res.mjs +1 -4
@@ -12,17 +12,26 @@ let log = Logger.fromEnv()
12
12
  // fragments. These SDL declarations are injected into any fragment that emits
13
13
  // at least one mutation field; the stitcher dedupes by type name so
14
14
  // concatenating across plugin + admin fragments stays safe.
15
+ //
16
+ // `cause` says which no a rejection is: the model's (DOMAIN, `errorCode` is the
17
+ // declared error), the caller's (ACCESS) or the platform's (INFRASTRUCTURE).
15
18
  let commandResultSdlTypes: array<string> = [
16
19
  `union CommandResult = CommandAccepted | CommandRejected | CommandPending`,
17
20
  `type CommandAccepted {
18
21
  msgId: ID!
19
22
  entityId: ID
20
23
  eventCount: Int!
24
+ }`,
25
+ `enum CommandRejectionCause {
26
+ DOMAIN
27
+ ACCESS
28
+ INFRASTRUCTURE
21
29
  }`,
22
30
  `type CommandRejected {
23
31
  msgId: ID!
24
32
  errorCode: String!
25
33
  errorDetail: String
34
+ cause: CommandRejectionCause!
26
35
  }`,
27
36
  `type CommandPending {
28
37
  msgId: ID!
@@ -22,11 +22,17 @@ let commandResultSdlTypes = [
22
22
  msgId: ID!
23
23
  entityId: ID
24
24
  eventCount: Int!
25
+ }`,
26
+ `enum CommandRejectionCause {
27
+ DOMAIN
28
+ ACCESS
29
+ INFRASTRUCTURE
25
30
  }`,
26
31
  `type CommandRejected {
27
32
  msgId: ID!
28
33
  errorCode: String!
29
34
  errorDetail: String
35
+ cause: CommandRejectionCause!
30
36
  }`,
31
37
  `type CommandPending {
32
38
  msgId: ID!
@@ -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: `queryableDef.schema`,
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.effectHandler<
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.effectHandler<CommandGenerator.payload, 'context, CommandTopic.commandOutcome, unit>,
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.effectHandler<callbackEvent, 'context, unit, string>>
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.effectHandler<callbackEvent, 'context, unit, string>,
56
+ Runtime.effectInvocationHandler<callbackEvent, 'context, unit, string>,
57
57
  >
58
58
 
59
59
  let make: (
@@ -23,6 +23,8 @@ function filter(allCommandTopics, names) {
23
23
 
24
24
  let componentType = "CommandTopic";
25
25
 
26
+ let refusalCauseToGraphQL = CommandTopic_Helpers$ReventlessCore.refusalCauseToGraphQL;
27
+
26
28
  let commandOutcomeHook = CommandTopic_Helpers$ReventlessCore.commandOutcomeHook;
27
29
 
28
30
  let registerCommandOutcome = CommandTopic_Helpers$ReventlessCore.registerCommandOutcome;
@@ -60,6 +62,7 @@ let getHandlers = CommandTopic_Helpers$ReventlessCore.getHandlers;
60
62
  export {
61
63
  componentType,
62
64
  NotPublishedToChannel,
65
+ refusalCauseToGraphQL,
63
66
  commandOutcomeHook,
64
67
  registerCommandOutcome,
65
68
  clearCommandOutcome,
@@ -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.effectHandler<'callbackEvent, 'context, unit, string>,
15
+ Runtime.effectInvocationHandler<'callbackEvent, 'context, unit, string>,
16
16
  >,
17
17
  connect: connect<'callbackEvent, 'context, 'channelParts, 'runtimeParts>,
18
18
  }
@@ -31,6 +31,14 @@ own what the command acts on (`OwnerScope.decideActing`).
31
31
  */
32
32
  type refusalCause = DomainRejection | InfrastructureFailure | AccessRefusal
33
33
 
34
+ /** The `CommandRejectionCause` enum value a client sees for a cause. */
35
+ let refusalCauseToGraphQL = (cause: refusalCause): string =>
36
+ switch cause {
37
+ | DomainRejection => "DOMAIN"
38
+ | AccessRefusal => "ACCESS"
39
+ | InfrastructureFailure => "INFRASTRUCTURE"
40
+ }
41
+
34
42
  /**
35
43
  What became of one command. Constructors are prefixed to stay clear of `commandOutcome`'s
36
44
  `Accepted`/`Rejected` below — that type is the producer-facing result shape (and the GraphQL
@@ -91,10 +99,9 @@ let fireCommandOutcome = (~component: string, ~reference: string, outcome: obser
91
99
  // Callback implementations call reportAccepted during inline dispatch.
92
100
  let acceptedResultChannel: ref<option<(string, acceptedResult) => unit>> = ref(None)
93
101
 
94
- // Sibling side-channel for domain rejections — populated by callbacks when
95
- // `Decision.decide` returns `Error`. Read by runInlineAndCollect to build
96
- // `Rejected` outcomes that carry the real error code and detail.
97
- let rejectedResultChannel: ref<option<(string, rejectedResult) => unit>> = ref(None)
102
+ // Sibling side-channel for rejections, with their cause. Read by runInlineAndCollect
103
+ // to build `Rejected` outcomes that carry the real error code, detail and cause.
104
+ let rejectedResultChannel: ref<option<(string, rejectedResult, refusalCause) => unit>> = ref(None)
98
105
 
99
106
  /**
100
107
  Turn an `acting` verdict into the refusal a command handler reports, or `None` to let
@@ -112,7 +119,10 @@ let ownershipRefusal = (
112
119
  ~command: string,
113
120
  ): option<ownershipRefusal> => {
114
121
  let refuse = (~isDefect=false, why) => Some({
115
- rejected: {errorCode: "Forbidden", errorDetail: `${component}.${command}: ${why}`},
122
+ rejected: {
123
+ errorCode: Reventless.CommandOutcome.code(Forbidden),
124
+ errorDetail: `${component}.${command}: ${why}`,
125
+ },
116
126
  isDefect,
117
127
  })
118
128
  switch verdict {
@@ -148,7 +158,7 @@ let reportRejected = (
148
158
  reference: string,
149
159
  result: rejectedResult,
150
160
  ) => {
151
- rejectedResultChannel.contents->Option.forEach(cb => cb(reference, result))
161
+ rejectedResultChannel.contents->Option.forEach(cb => cb(reference, result, cause))
152
162
  fireCommandOutcome(
153
163
  ~component,
154
164
  ~reference,
@@ -160,7 +170,7 @@ let reportRejected = (
160
170
  // without importing Adapter.res → @pulumi/pulumi. Re-exported by CommandTopic via include.
161
171
  type commandOutcome =
162
172
  | Accepted({msgId: string, entityId?: string, eventCount: int})
163
- | Rejected({msgId: string, errorCode: string, errorDetail: option<string>})
173
+ | Rejected({msgId: string, errorCode: string, errorDetail: option<string>, cause: refusalCause})
164
174
  | Pending({msgId: string})
165
175
 
166
176
  // Serialise a commandOutcome to the JSON shape the CommandResult GraphQL union
@@ -179,13 +189,14 @@ let commandOutcomeToJson = (outcome: commandOutcome): JSON.t =>
179
189
  ("eventCount", JSON.Number(eventCount->Int.toFloat)),
180
190
  ]),
181
191
  )
182
- | Rejected({msgId, errorCode, errorDetail}) =>
192
+ | Rejected({msgId, errorCode, errorDetail, cause}) =>
183
193
  JSON.Object(
184
194
  Dict.fromArray([
185
195
  ("__typename", JSON.String("CommandRejected")),
186
196
  ("msgId", JSON.String(msgId)),
187
197
  ("errorCode", JSON.String(errorCode)),
188
198
  ("errorDetail", errorDetail->Option.map(s => JSON.String(s))->Option.getOr(JSON.Null)),
199
+ ("cause", JSON.String(refusalCauseToGraphQL(cause))),
189
200
  ]),
190
201
  )
191
202
  | Pending({msgId}) =>
@@ -222,27 +233,34 @@ let runInlineAndCollect = async (
222
233
  item
223
234
  })
224
235
  let acceptedResults: dict<acceptedResult> = Dict.make()
225
- let rejectedResults: dict<rejectedResult> = Dict.make()
236
+ let rejectedResults: dict<(rejectedResult, refusalCause)> = Dict.make()
226
237
  acceptedResultChannel.contents = Some(
227
238
  (reference, result) => acceptedResults->Dict.set(reference, result),
228
239
  )
229
240
  rejectedResultChannel.contents = Some(
230
- (reference, result) => rejectedResults->Dict.set(reference, result),
241
+ (reference, result, cause) => rejectedResults->Dict.set(reference, (result, cause)),
231
242
  )
232
243
  let results = await handleCmds(Stream.fromIterable(items))->Effect.runPromise
233
244
  acceptedResultChannel.contents = None
234
245
  rejectedResultChannel.contents = None
235
246
  jsons->Array.mapWithIndex((cmdJson, i) => {
236
247
  let msgId = cmdJson.meta.msgId
237
- // Domain rejection from Decision.decide takes precedence over both Accepted
238
- // and the synthesized "Conflict" — it carries the most informative error.
248
+ // A reported rejection takes precedence over both Accepted and the
249
+ // synthesized "Conflict" — it carries the most informative error.
239
250
  switch rejectedResults->Dict.get(msgId) {
240
- | Some({errorCode, errorDetail}) =>
251
+ | Some(({errorCode, errorDetail}, cause)) =>
241
252
  let detail = errorDetail == "" ? None : Some(errorDetail)
242
- Rejected({msgId, errorCode, errorDetail: detail})
253
+ Rejected({msgId, errorCode, errorDetail: detail, cause})
243
254
  | None =>
244
255
  switch results->Array.get(i) {
245
- | Some(Error(msg)) => Rejected({msgId, errorCode: "Conflict", errorDetail: Some(msg)})
256
+ // The handler failed outright: the platform, not the model.
257
+ | Some(Error(msg)) =>
258
+ Rejected({
259
+ msgId,
260
+ errorCode: Reventless.CommandOutcome.code(Conflict),
261
+ errorDetail: Some(msg),
262
+ cause: InfrastructureFailure,
263
+ })
246
264
  | _ =>
247
265
  let ar = acceptedResults->Dict.get(msgId)->Option.getOr({eventCount: 0})
248
266
  switch ar.entityId {
@@ -8,8 +8,20 @@ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/component
8
8
  import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
9
9
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
10
10
  import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
11
+ import * as CommandOutcome$Reventless from "@reventlessdev/reventless-spec/src/components/CommandOutcome.res.mjs";
11
12
  import * as EffectLogger$ReventlessCore from "../../util/EffectLogger.res.mjs";
12
13
 
14
+ function refusalCauseToGraphQL(cause) {
15
+ switch (cause) {
16
+ case "DomainRejection" :
17
+ return "DOMAIN";
18
+ case "InfrastructureFailure" :
19
+ return "INFRASTRUCTURE";
20
+ case "AccessRefusal" :
21
+ return "ACCESS";
22
+ }
23
+ }
24
+
13
25
  let commandOutcomeHook = {
14
26
  contents: undefined
15
27
  };
@@ -51,7 +63,7 @@ function ownershipRefusal(verdict, component, command) {
51
63
  let isDefect = isDefectOpt !== undefined ? isDefectOpt : false;
52
64
  return {
53
65
  rejected: {
54
- errorCode: "Forbidden",
66
+ errorCode: CommandOutcome$Reventless.code("Forbidden"),
55
67
  errorDetail: component + `.` + command + `: ` + why
56
68
  },
57
69
  isDefect: isDefect
@@ -84,7 +96,7 @@ function reportAccepted(component, reference, result) {
84
96
  }
85
97
 
86
98
  function reportRejected(component, cause, reference, result) {
87
- Stdlib_Option.forEach(rejectedResultChannel.contents, cb => cb(reference, result));
99
+ Stdlib_Option.forEach(rejectedResultChannel.contents, cb => cb(reference, result, cause));
88
100
  fireCommandOutcome(component, reference, {
89
101
  TAG: "OutcomeRejected",
90
102
  errorCode: result.errorCode,
@@ -131,6 +143,10 @@ function commandOutcomeToJson(outcome) {
131
143
  [
132
144
  "errorDetail",
133
145
  Stdlib_Option.getOr(Stdlib_Option.map(outcome.errorDetail, s => (s)), null)
146
+ ],
147
+ [
148
+ "cause",
149
+ refusalCauseToGraphQL(outcome.cause)
134
150
  ]
135
151
  ]);
136
152
  case "Pending" :
@@ -177,8 +193,11 @@ async function runInlineAndCollect(jsons, handleCmds) {
177
193
  acceptedResultChannel.contents = (reference, result) => {
178
194
  acceptedResults[reference] = result;
179
195
  };
180
- rejectedResultChannel.contents = (reference, result) => {
181
- rejectedResults[reference] = result;
196
+ rejectedResultChannel.contents = (reference, result, cause) => {
197
+ rejectedResults[reference] = [
198
+ result,
199
+ cause
200
+ ];
182
201
  };
183
202
  let results = await Effect.runPromise(handleCmds(Stream.fromIterable(items)));
184
203
  acceptedResultChannel.contents = undefined;
@@ -187,22 +206,25 @@ async function runInlineAndCollect(jsons, handleCmds) {
187
206
  let msgId = cmdJson.meta.msgId;
188
207
  let match = rejectedResults[msgId];
189
208
  if (match !== undefined) {
190
- let errorDetail = match.errorDetail;
209
+ let match$1 = match[0];
210
+ let errorDetail = match$1.errorDetail;
191
211
  let detail = errorDetail === "" ? undefined : errorDetail;
192
212
  return {
193
213
  TAG: "Rejected",
194
214
  msgId: msgId,
195
- errorCode: match.errorCode,
196
- errorDetail: detail
215
+ errorCode: match$1.errorCode,
216
+ errorDetail: detail,
217
+ cause: match[1]
197
218
  };
198
219
  }
199
- let match$1 = results[i];
200
- if (match$1 !== undefined && match$1.TAG !== "Ok") {
220
+ let match$2 = results[i];
221
+ if (match$2 !== undefined && match$2.TAG !== "Ok") {
201
222
  return {
202
223
  TAG: "Rejected",
203
224
  msgId: msgId,
204
- errorCode: "Conflict",
205
- errorDetail: match$1._0
225
+ errorCode: CommandOutcome$Reventless.code("Conflict"),
226
+ errorDetail: match$2._0,
227
+ cause: "InfrastructureFailure"
206
228
  };
207
229
  }
208
230
  let ar = Stdlib_Option.getOr(acceptedResults[msgId], {
@@ -249,6 +271,7 @@ function getHandlers(typeName) {
249
271
  }
250
272
 
251
273
  export {
274
+ refusalCauseToGraphQL,
252
275
  commandOutcomeHook,
253
276
  registerCommandOutcome,
254
277
  clearCommandOutcome,
@@ -24,7 +24,7 @@ module type T = {
24
24
  let makeHandler: (
25
25
  ~eventCollector: component,
26
26
  ~jsonEventsHandler: jsonEventsHandler,
27
- ) => Pulumi.Output.t<Runtime.effectHandler<callbackEvent, 'context, unit, string>>
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.effectHandler<'callbackEvent, 'context, unit, string>,
6
+ Runtime.effectInvocationHandler<'callbackEvent, 'context, unit, string>,
7
7
  >,
8
8
  }
9
9
 
@@ -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.eventHandler<unit, 'context, unit>>
22
+ ) => Pulumi.Output.t<Runtime.invocationHandler<unit, 'context, unit>>
23
23
 
24
24
  let make: (~name: string, ~opts: Pulumi.ComponentResource.options=?) => component
25
25
  }
@@ -19,6 +19,17 @@ type auditRow = {
19
19
  door?: ReventlessInfra.InboundTranslationSlice.door,
20
20
  }
21
21
 
22
+ /** A refused caller is access, a failed publish the platform; anything else is
23
+ the translation refusing its input. */
24
+ let rejectionCauseOf = (errorCode: string): CommandTopic_Helpers.refusalCause =>
25
+ if errorCode == Reventless.CommandOutcome.code(Forbidden) {
26
+ AccessRefusal
27
+ } else if errorCode == Reventless.CommandOutcome.code(PublishFailed) {
28
+ InfrastructureFailure
29
+ } else {
30
+ DomainRejection
31
+ }
32
+
22
33
  /** The `CommandResult` a `receive` outcome answers with: the first target as
23
34
  `entityId` (omitted when nothing was produced), the fan-out as `eventCount`. */
24
35
  let receiveResultToOutcome = (
@@ -31,10 +42,12 @@ let receiveResultToOutcome = (
31
42
  | None => Accepted({msgId: requestId, eventCount: commandCount})
32
43
  }
33
44
  | Error({requestId, error, ?errorCode}) =>
45
+ let errorCode = errorCode->Option.getOr(Reventless.CommandOutcome.code(TranslationFailed))
34
46
  Rejected({
35
47
  msgId: requestId,
36
- errorCode: errorCode->Option.getOr("TranslationFailed"),
48
+ errorCode,
37
49
  errorDetail: Some(error),
50
+ cause: rejectionCauseOf(errorCode),
38
51
  })
39
52
  }
40
53
 
@@ -216,7 +229,7 @@ module Make = (
216
229
  )
217
230
  ) =>
218
231
  fail(
219
- ~errorCode="Forbidden",
232
+ ~errorCode=Reventless.CommandOutcome.code(Forbidden),
220
233
  `${Spec.name}: the caller is not authorized for every command this input translates into`,
221
234
  )
222
235
  | Ok([]) => succeed([])
@@ -225,7 +238,11 @@ module Make = (
225
238
  await publishJsons(msgs)
226
239
  succeed(pairs->Array.map(((targetId, _)) => targetId))
227
240
  } catch {
228
- | exn => fail(~errorCode="PublishFailed", messageOf(exn, "publish failed"))
241
+ | exn =>
242
+ fail(
243
+ ~errorCode=Reventless.CommandOutcome.code(PublishFailed),
244
+ messageOf(exn, "publish failed"),
245
+ )
229
246
  }
230
247
  }
231
248
  }
@@ -16,6 +16,7 @@ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/U
16
16
  import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
17
17
  import * as Message$ReventlessCore from "../../Message.res.mjs";
18
18
  import * as Authorization$Reventless from "@reventlessdev/reventless-spec/src/types/Authorization.res.mjs";
19
+ import * as CommandOutcome$Reventless from "@reventlessdev/reventless-spec/src/components/CommandOutcome.res.mjs";
19
20
  import * as EffectLogger$ReventlessCore from "../../util/EffectLogger.res.mjs";
20
21
  import * as ApiNoApiHelpers$ReventlessCore from "../Api/ApiNoApiHelpers.res.mjs";
21
22
  import * as InboundTranslationSlice$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/InboundTranslationSlice.res.mjs";
@@ -36,6 +37,16 @@ let auditRowSchema = Sury.$schema(s => ({
36
37
  door: s.m(Sury.$option(InboundTranslationSlice$ReventlessInfra.doorSchema))
37
38
  }));
38
39
 
40
+ function rejectionCauseOf(errorCode) {
41
+ if (errorCode === CommandOutcome$Reventless.code("Forbidden")) {
42
+ return "AccessRefusal";
43
+ } else if (errorCode === CommandOutcome$Reventless.code("PublishFailed")) {
44
+ return "InfrastructureFailure";
45
+ } else {
46
+ return "DomainRejection";
47
+ }
48
+ }
49
+
39
50
  function receiveResultToOutcome(result) {
40
51
  if (result.TAG === "Ok") {
41
52
  let match = result._0;
@@ -58,11 +69,13 @@ function receiveResultToOutcome(result) {
58
69
  }
59
70
  }
60
71
  let match$1 = result._0;
72
+ let errorCode = Stdlib_Option.getOr(match$1.errorCode, CommandOutcome$Reventless.code("TranslationFailed"));
61
73
  return {
62
74
  TAG: "Rejected",
63
75
  msgId: match$1.requestId,
64
- errorCode: Stdlib_Option.getOr(match$1.errorCode, "TranslationFailed"),
65
- errorDetail: match$1.error
76
+ errorCode: errorCode,
77
+ errorDetail: match$1.error,
78
+ cause: rejectionCauseOf(errorCode)
66
79
  };
67
80
  }
68
81
 
@@ -213,7 +226,7 @@ function Make(Spec) {
213
226
  }
214
227
  let msgs = msg$1._0;
215
228
  if (msgs.some(param => !callerAdmits(Authorization$Reventless.named(Spec.authorizationOf(Message$Reventless.variantNameOfJson(param.commandJson))), caller))) {
216
- return fail("Forbidden", Spec.name + `: the caller is not authorized for every command this input translates into`);
229
+ return fail(CommandOutcome$Reventless.code("Forbidden"), Spec.name + `: the caller is not authorized for every command this input translates into`);
217
230
  }
218
231
  if (msgs.length === 0) {
219
232
  return succeed([]);
@@ -223,7 +236,7 @@ function Make(Spec) {
223
236
  return succeed(pairs.map(param => param[0]));
224
237
  } catch (raw_exn$1) {
225
238
  let exn$1 = Primitive_exceptions.internalToException(raw_exn$1);
226
- return fail("PublishFailed", messageOf(exn$1, "publish failed"));
239
+ return fail(CommandOutcome$Reventless.code("PublishFailed"), messageOf(exn$1, "publish failed"));
227
240
  }
228
241
  };
229
242
  let refuse = (body, errorCode, error) => failWith(Uuid.v4(), body, "Webhook", errorCode, error);
@@ -241,6 +254,7 @@ function Make(Spec) {
241
254
  export {
242
255
  auditStatusSchema,
243
256
  auditRowSchema,
257
+ rejectionCauseOf,
244
258
  receiveResultToOutcome,
245
259
  requestIdOf,
246
260
  takeAuditRow,
@@ -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 `queryableDef.consumedEventTypes`. */
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>
@@ -478,10 +478,10 @@ module Make = (
478
478
  // which disagreed with the backends' casing).
479
479
  let (errorCode, errDetail) = switch err {
480
480
  | ReventlessInfra.DcbEventLog.Conflict => (
481
- "Conflict",
481
+ Reventless.CommandOutcome.code(Conflict),
482
482
  "conflict: condition check failed",
483
483
  )
484
- | StorageFailure(msg) => ("AppendFailed", msg)
484
+ | StorageFailure(msg) => (Reventless.CommandOutcome.code(AppendFailed), msg)
485
485
  }
486
486
  if retries > 0 {
487
487
  // Re-seed from the just-read snapshot so the retry reads only
@@ -504,7 +504,7 @@ module Make = (
504
504
  // A surfaced Conflict means the 3-retry loop could not absorb
505
505
  // the contention — the operator signal for "this slice is hot"
506
506
  // (consider sharding / async — Issue 10, §4 of the analysis).
507
- if errorCode == "Conflict" {
507
+ if errorCode == Reventless.CommandOutcome.code(Conflict) {
508
508
  Metrics.emitCount(~metric="AppendConflict", ~slice=Spec.name)
509
509
  }
510
510
  // Both arms here are storage, not the model: a surfaced Conflict is
@@ -17,6 +17,7 @@ import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types
17
17
  import * as Message$ReventlessCore from "../../Message.res.mjs";
18
18
  import * as Metrics$ReventlessCore from "../../util/Metrics.res.mjs";
19
19
  import * as LogFormat$ReventlessCore from "../../util/LogFormat.res.mjs";
20
+ import * as CommandOutcome$Reventless from "@reventlessdev/reventless-spec/src/components/CommandOutcome.res.mjs";
20
21
  import * as EffectLogger$ReventlessCore from "../../util/EffectLogger.res.mjs";
21
22
  import * as CommandTopic_Helpers$ReventlessCore from "../CommandTopic/CommandTopic_Helpers.res.mjs";
22
23
 
@@ -311,10 +312,10 @@ function Make(Spec) {
311
312
  let err = appendResult._0;
312
313
  let match;
313
314
  match = typeof err !== "object" ? [
314
- "Conflict",
315
+ CommandOutcome$Reventless.code("Conflict"),
315
316
  "conflict: condition check failed"
316
317
  ] : [
317
- "AppendFailed",
318
+ CommandOutcome$Reventless.code("AppendFailed"),
318
319
  err._0
319
320
  ];
320
321
  let errDetail = match[1];
@@ -329,7 +330,7 @@ function Make(Spec) {
329
330
  return Effect.flatMap(EffectLogger$ReventlessCore.logWarn(comp, undefined, `append failed (retrying ` + ((3 - retries | 0) + 1 | 0).toString() + `/` + (3).toString() + `): ` + errDetail), () => attempt(retries - 1 | 0));
330
331
  } else {
331
332
  cacheInvalidate();
332
- if (errorCode === "Conflict") {
333
+ if (errorCode === CommandOutcome$Reventless.code("Conflict")) {
333
334
  Metrics$ReventlessCore.emitCount("AppendConflict", Spec.name, undefined);
334
335
  }
335
336
  CommandTopic_Helpers$ReventlessCore.reportRejected(Spec.name, "InfrastructureFailure", cmdJson.meta.msgId, {
@@ -132,8 +132,8 @@ module Make = (
132
132
  })->Array.map(
133
133
  Reventless.Projection.mapActionId(
134
134
  _,
135
- ~to_=Spec.Key.toString,
136
- ~from=Spec.Key.makeFromString,
135
+ ~to_=Spec.Id.toString,
136
+ ~from=Spec.Id.makeFromString,
137
137
  ),
138
138
  ) catch {
139
139
  | exn =>
@@ -109,7 +109,7 @@ function Make(RuntimeEnvironment) {
109
109
  event: Primitive_option.valFromOption(event),
110
110
  meta: meta,
111
111
  recordedAt: recordedAt
112
- }).map(__x => Projection$Reventless.mapActionId(__x, Spec.Key.toString, Spec.Key.makeFromString));
112
+ }).map(__x => Projection$Reventless.mapActionId(__x, Spec.Id.toString, Spec.Id.makeFromString));
113
113
  } catch (raw_exn) {
114
114
  let exn$1 = Primitive_exceptions.internalToException(raw_exn);
115
115
  let errMsg = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn$1), Stdlib_JsExn.message), "unknown");
@@ -44,8 +44,8 @@ module Make = (
44
44
  Projection.project({event, meta: raw.meta, recordedAt: raw.recordedAt})->Array.map(
45
45
  Reventless.Projection.mapActionId(
46
46
  _,
47
- ~to_=Spec.Key.toString,
48
- ~from=Spec.Key.makeFromString,
47
+ ~to_=Spec.Id.toString,
48
+ ~from=Spec.Id.makeFromString,
49
49
  ),
50
50
  )
51
51
  let actionsStr = LogFormat.actionNames(actions)
@@ -33,7 +33,7 @@ function Make(Spec) {
33
33
  event: event,
34
34
  meta: raw.meta,
35
35
  recordedAt: raw.recordedAt
36
- }).map(__x => Projection$Reventless.mapActionId(__x, Spec.Key.toString, Spec.Key.makeFromString));
36
+ }).map(__x => Projection$Reventless.mapActionId(__x, Spec.Id.toString, Spec.Id.makeFromString));
37
37
  let actionsStr = LogFormat$ReventlessCore.actionNames(actions);
38
38
  Effect.runSync(EffectLogger$ReventlessCore.logInfo(comp, raw.data, `handling event ` + idx.contents.toString() + `/` + count + `: ` + LogFormat$ReventlessCore.bold(raw.eventType) + `(` + id + `) ` + actionsStr));
39
39
  allActions.push(...actions.map(__x => Projection$ReventlessCore.rewriteAction(__x, raw.meta.time, Spec.stateSchema)));
@@ -24,7 +24,7 @@ module type Bucket = {
24
24
  type bucketParts
25
25
 
26
26
  let connect: connect<bucketParts, runtimeParts>
27
- let makeHandler: Task.bucketCallback => Runtime.eventHandler<
27
+ let makeHandler: Task.bucketCallback => Runtime.invocationHandler<
28
28
  callbackEvent,
29
29
  context,
30
30
  array<Task.taskAction>,
@@ -97,7 +97,7 @@ module Make = (
97
97
  ): Task.T => {
98
98
  module Spec = Spec
99
99
  type component = Task.component
100
- // type handler = Runtime.eventHandler<
100
+ // type handler = Runtime.invocationHandler<
101
101
  // TaskRuntimeBuilder.callbackEvent,
102
102
  // TaskRuntimeBuilder.context,
103
103
  // array<Task.taskAction>,