@reventlessdev/reventless-core 3.0.0-alpha.291 → 3.0.0-alpha.292

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,16 @@
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.292 (2026-10-10)
7
+
8
+ ### Bug Fixes
9
+
10
+ * an inbound translation is gated alike on every door, and encodes its commands by schema ([6ec3ce6](https://github.com/ReventlessDev/reventless-core/commit/6ec3ce672e53e2c162c64a90d103be691391be5e))
11
+ ### Features
12
+
13
+ * **gwt:** automation and translation tests take the slice as written, and the sidecars read them ([93a0919](https://github.com/ReventlessDev/reventless-core/commit/93a0919a0d66730cf9bf89144ad8fba3cb025332))
14
+
15
+
6
16
  # 3.0.0-alpha.291 (2026-10-08)
7
17
 
8
18
  **Note:** Version bump only for package @reventlessdev/reventless-core
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.291",
3
+ "version": "3.0.0-alpha.292",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -30,15 +30,15 @@
30
30
  "uuid": "^13.0.0",
31
31
  "@reventlessdev/rescript-effect": "0.1.0-alpha.34",
32
32
  "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.22",
33
- "@reventlessdev/rescript-jest": "1.0.0-alpha.11",
34
- "@reventlessdev/rescript-node": "2.0.0-alpha.22",
35
33
  "@reventlessdev/rescript-hash-object": "1.2.0-alpha.15",
36
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.23",
34
+ "@reventlessdev/rescript-node": "2.0.0-alpha.22",
35
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.11",
37
36
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
38
- "@reventlessdev/rescript-ssh2": "2.0.0-alpha.23",
39
- "@reventlessdev/reventless-infra": "3.0.0-alpha.188",
37
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.23",
38
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.189",
40
39
  "@reventlessdev/reventless-interop": "3.0.0-alpha.39",
41
- "@reventlessdev/reventless-spec": "3.0.0-alpha.160"
40
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.161",
41
+ "@reventlessdev/rescript-ssh2": "2.0.0-alpha.23"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "12.3.1",
@@ -166,50 +166,33 @@ module Make = (
166
166
  None
167
167
  }
168
168
 
169
- // Per-source erased dispatch — pre-compile decoders once at module init.
170
- type dispatch = {
171
- sourceName: string,
172
- handle: (JSON.t, ~sourceId: string, Reventless.AutomationSlice.context) => unit,
173
- }
169
+ module Route = AutomationSlice_Route.Make(Spec, Automation)
174
170
 
175
- let dispatches: array<
176
- dispatch,
177
- > = Automation.mappings->Array.map((module(M: Automation.Mapping)) => {
178
- let decoder = Reventless.DcbDecode.makeDecoder(M.sourceEventSchema)
179
- let handle = (json: JSON.t, ~sourceId: string, ctx: Reventless.AutomationSlice.context) => {
180
- let (eventType, dataDict) = json->Message.splitMessage
181
- switch decoder.decode(~eventType, ~data=dataDict) {
182
- | Some(event) =>
183
- // Collect — append new items keyed by ID; first writer wins (idempotent).
184
- M.collect(event, ~sourceId, ctx)->Array.forEach(((id, item)) => {
185
- switch todoItems->Dict.get(id) {
186
- | Some(_) => () // Already exists — skip
187
- | None =>
188
- let row: todoRow = {
189
- item: item->Reventless.Util_Sury.toJson(Spec.todoItemSchema),
190
- status: Pending,
191
- createdAt: now(),
192
- retryCount: 0,
193
- maxRetries: Spec.maxRetries,
194
- }
195
- todoItems->Dict.set(id, row)
196
- }
197
- })
198
- // Resolve — mark a pending item as completed.
199
- switch M.resolve(event) {
200
- | Some(id) =>
201
- todoItems
202
- ->Dict.get(id)
203
- ->Option.forEach(row => {
204
- todoItems->Dict.set(id, {...row, status: Completed, completedAt: now()})
205
- })
206
- | None => ()
171
+ let apply = ({collected, resolved}: AutomationSlice_Route.routed<Spec.todoItem>) => {
172
+ // Collect — append new items keyed by ID; first writer wins (idempotent).
173
+ collected->Array.forEach(((id, item)) => {
174
+ switch todoItems->Dict.get(id) {
175
+ | Some(_) => () // Already exists — skip
176
+ | None =>
177
+ let row: todoRow = {
178
+ item: item->Reventless.Util_Sury.toJson(Spec.todoItemSchema),
179
+ status: Pending,
180
+ createdAt: now(),
181
+ retryCount: 0,
182
+ maxRetries: Spec.maxRetries,
207
183
  }
208
- | None => ()
184
+ todoItems->Dict.set(id, row)
209
185
  }
210
- }
211
- {sourceName: M.sourceName, handle}
212
- })
186
+ })
187
+ // Resolve — mark a pending item as completed.
188
+ resolved->Option.forEach(id =>
189
+ todoItems
190
+ ->Dict.get(id)
191
+ ->Option.forEach(row => {
192
+ todoItems->Dict.set(id, {...row, status: Completed, completedAt: now()})
193
+ })
194
+ )
195
+ }
213
196
 
214
197
  let phase1 = (events: array<JSON.t>, ctx: Reventless.AutomationSlice.context) => {
215
198
  events->Array.forEach(json => {
@@ -223,11 +206,7 @@ module Make = (
223
206
  | None => json
224
207
  }
225
208
  // Multiple mappings can share a sourceName; all matching dispatches run.
226
- dispatches->Array.forEach(d => {
227
- if d.sourceName == sourceName {
228
- d.handle(eventPayload, ~sourceId=context.id, ctx)
229
- }
230
- })
209
+ Route.route(eventPayload, ~sourceName, ~sourceId=context.id, ctx)->Array.forEach(apply)
231
210
  })
232
211
  }
233
212
 
@@ -8,11 +8,11 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
8
  import * as Effect from "effect/Effect";
9
9
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
10
10
  import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
11
- import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/components/DcbDecode.res.mjs";
12
11
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
13
12
  import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
14
13
  import * as Message$ReventlessCore from "../../Message.res.mjs";
15
14
  import * as EffectLogger$ReventlessCore from "../../util/EffectLogger.res.mjs";
15
+ import * as AutomationSlice_Route$ReventlessCore from "./AutomationSlice_Route.res.mjs";
16
16
 
17
17
  let todoStatusSchema = Sury.union([
18
18
  Sury.literal("Pending"),
@@ -55,59 +55,40 @@ function Make(Spec) {
55
55
  newrecord.status = retryCount >= Spec.maxRetries ? "Abandoned" : "Failed";
56
56
  return newrecord;
57
57
  };
58
- let dispatches = Automation.mappings.map(M => {
59
- let decoder = DcbDecode$Reventless.makeDecoder(M.sourceEventSchema);
60
- let handle = (json, sourceId, ctx) => {
61
- let match = Message$ReventlessCore.splitMessage(json);
62
- let event = decoder.decode(match[0], match[1]);
63
- if (event === undefined) {
58
+ let Route = AutomationSlice_Route$ReventlessCore.Make(Spec)(Automation);
59
+ let apply = param => {
60
+ param.collected.forEach(param => {
61
+ let id = param[0];
62
+ let match = todoItems[id];
63
+ if (match !== undefined) {
64
64
  return;
65
65
  }
66
- let event$1 = Primitive_option.valFromOption(event);
67
- M.collect(event$1, sourceId, ctx).forEach(param => {
68
- let id = param[0];
69
- let match = todoItems[id];
70
- if (match !== undefined) {
71
- return;
72
- }
73
- let row_item = Util_Sury$Reventless.toJson(param[1], Spec.todoItemSchema);
74
- let row_createdAt = new Date().toISOString();
75
- let row_maxRetries = Spec.maxRetries;
76
- let row = {
77
- item: row_item,
78
- status: "Pending",
79
- createdAt: row_createdAt,
80
- retryCount: 0,
81
- maxRetries: row_maxRetries
82
- };
83
- todoItems[id] = row;
84
- });
85
- let id = M.resolve(event$1);
86
- if (id !== undefined) {
87
- return Stdlib_Option.forEach(todoItems[id], row => {
88
- let newrecord = {...row};
89
- newrecord.completedAt = new Date().toISOString();
90
- newrecord.status = "Completed";
91
- todoItems[id] = newrecord;
92
- });
93
- }
94
- };
95
- return {
96
- sourceName: M.sourceName,
97
- handle: handle
98
- };
99
- });
66
+ let row_item = Util_Sury$Reventless.toJson(param[1], Spec.todoItemSchema);
67
+ let row_createdAt = new Date().toISOString();
68
+ let row_maxRetries = Spec.maxRetries;
69
+ let row = {
70
+ item: row_item,
71
+ status: "Pending",
72
+ createdAt: row_createdAt,
73
+ retryCount: 0,
74
+ maxRetries: row_maxRetries
75
+ };
76
+ todoItems[id] = row;
77
+ });
78
+ Stdlib_Option.forEach(param.resolved, id => Stdlib_Option.forEach(todoItems[id], row => {
79
+ let newrecord = {...row};
80
+ newrecord.completedAt = new Date().toISOString();
81
+ newrecord.status = "Completed";
82
+ todoItems[id] = newrecord;
83
+ }));
84
+ };
100
85
  let phase1 = (events, ctx) => {
101
86
  events.forEach(json => {
102
87
  let context = Message$ReventlessCore.decode(json, Message$Reventless.contextSchema);
103
88
  let sourceName = context.meta.service;
104
89
  let dict = Stdlib_JSON.Decode.object(json);
105
90
  let eventPayload = dict !== undefined ? Stdlib_Option.getOr(dict["event"], json) : json;
106
- dispatches.forEach(d => {
107
- if (d.sourceName === sourceName) {
108
- return d.handle(eventPayload, context.id, ctx);
109
- }
110
- });
91
+ Route.route(eventPayload, sourceName, context.id, ctx).forEach(apply);
111
92
  });
112
93
  };
113
94
  let phase2 = async publishJsons => {
@@ -0,0 +1,50 @@
1
+ // Decode one source event and run its mappings' `collect`/`resolve`, with no
2
+ // side effects. Shared by the runtime callback, which applies the result to its
3
+ // to-do list, and by the GWT sweep, which must route exactly as the runtime does.
4
+
5
+ /** What one mapping made of one event. */
6
+ type routed<'item> = {
7
+ collected: array<(string, 'item)>,
8
+ resolved: option<string>,
9
+ }
10
+
11
+ module Make = (
12
+ Spec: Reventless.AutomationSlice.Spec,
13
+ Automation: Reventless.AutomationSlice.Automation with module Spec := Spec,
14
+ ) => {
15
+ type dispatch = {
16
+ sourceName: string,
17
+ handle: (
18
+ JSON.t,
19
+ ~sourceId: string,
20
+ Reventless.AutomationSlice.context,
21
+ ) => option<routed<Spec.todoItem>>,
22
+ }
23
+
24
+ // Decoders are compiled once, per mapping.
25
+ let dispatches: array<
26
+ dispatch,
27
+ > = Automation.mappings->Array.map((module(M: Automation.Mapping)) => {
28
+ let decoder = Reventless.DcbDecode.makeDecoder(M.sourceEventSchema)
29
+ let handle = (json: JSON.t, ~sourceId: string, ctx: Reventless.AutomationSlice.context) => {
30
+ let (eventType, dataDict) = json->Message.splitMessage
31
+ decoder.decode(~eventType, ~data=dataDict)->Option.map(event => {
32
+ collected: M.collect(event, ~sourceId, ctx),
33
+ resolved: M.resolve(event),
34
+ })
35
+ }
36
+ {sourceName: M.sourceName, handle}
37
+ })
38
+
39
+ /** Every mapping registered for `sourceName` that understood the payload, in
40
+ registration order. `payload` is the inner event, not the envelope. */
41
+ let route = (
42
+ payload: JSON.t,
43
+ ~sourceName: string,
44
+ ~sourceId: string,
45
+ ctx: Reventless.AutomationSlice.context,
46
+ ): array<routed<Spec.todoItem>> =>
47
+ dispatches->Array.filterMap(d =>
48
+ d.sourceName == sourceName ? d.handle(payload, ~sourceId, ctx) : None
49
+ )
50
+ }
@@ -0,0 +1,39 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/components/DcbDecode.res.mjs";
6
+ import * as Message$ReventlessCore from "../../Message.res.mjs";
7
+
8
+ function Make(Spec) {
9
+ return Automation => {
10
+ let dispatches = Automation.mappings.map(M => {
11
+ let decoder = DcbDecode$Reventless.makeDecoder(M.sourceEventSchema);
12
+ let handle = (json, sourceId, ctx) => {
13
+ let match = Message$ReventlessCore.splitMessage(json);
14
+ return Stdlib_Option.map(decoder.decode(match[0], match[1]), event => ({
15
+ collected: M.collect(event, sourceId, ctx),
16
+ resolved: M.resolve(event)
17
+ }));
18
+ };
19
+ return {
20
+ sourceName: M.sourceName,
21
+ handle: handle
22
+ };
23
+ });
24
+ let route = (payload, sourceName, sourceId, ctx) => Stdlib_Array.filterMap(dispatches, d => {
25
+ if (d.sourceName === sourceName) {
26
+ return d.handle(payload, sourceId, ctx);
27
+ }
28
+ });
29
+ return {
30
+ dispatches: dispatches,
31
+ route: route
32
+ };
33
+ };
34
+ }
35
+
36
+ export {
37
+ Make,
38
+ }
39
+ /* DcbDecode-Reventless Not a pure module */
@@ -89,6 +89,16 @@ let emptyResult: dcbResult = {
89
89
  eventLogEntries: [],
90
90
  }
91
91
 
92
+ // An inbound slice's door rule, read off its spec.
93
+ let inboundDoorPermission = (
94
+ commandSchema: S.t<'command>,
95
+ commandAuthorization: 'command => Reventless.Authorization.rule<'role>,
96
+ ) =>
97
+ Plugin_Structure.inboundDoorPermission(
98
+ ~commandSchema=commandSchema->S.castToUnknown,
99
+ ~commandAuthorization=cmd => cmd->commandAuthorization->Reventless.Authorization.named,
100
+ )
101
+
92
102
  module Make = (
93
103
  DcbEventLogStorage: DcbEventLog_Adapter.Storage,
94
104
  DcbEventTopicPublisher: EventTopic_Adapter.Publisher,
@@ -749,6 +759,10 @@ module Make = (
749
759
  registerResolver(
750
760
  ~fieldName,
751
761
  ~externalInputSchema=ITS.Spec.externalInputSchema->S.castToUnknown,
762
+ ~permission=?inboundDoorPermission(
763
+ ITS.Spec.commandSchema,
764
+ ITS.Spec.commandAuthorization,
765
+ ),
752
766
  )
753
767
  | None => ()
754
768
  }
@@ -865,16 +879,14 @@ module Make = (
865
879
  let raw: dict<JSON.t> = event->Obj.magic
866
880
  switch raw->Dict.get("__inboundTranslation") {
867
881
  | Some(_) =>
868
- let fieldName =
869
- raw
870
- ->Dict.get("fieldName")
871
- ->Option.flatMap(JSON.Decode.string)
872
- ->Option.getOr("")
873
- let args = raw->Dict.get("arguments")->Option.getOr(JSON.Encode.null)
874
- switch receivers->Dict.get(fieldName) {
882
+ let invocation: InboundTranslationSlice_Callback.doorInvocation = event->Obj.magic
883
+ switch receivers->Dict.get(invocation.fieldName) {
875
884
  | Some(receiveFn) =>
876
885
  Effect.promise(async () => {
877
- let result = await receiveFn(args)
886
+ let result = await receiveFn(
887
+ invocation.arguments,
888
+ ~caller=InboundTranslationSlice_Callback.doorCaller(invocation.identity),
889
+ )
878
890
  let response =
879
891
  result
880
892
  ->InboundTranslationSlice_Callback.receiveResultToOutcome
@@ -1034,33 +1046,9 @@ module Make = (
1034
1046
  })
1035
1047
  }
1036
1048
 
1037
- // DCB-specific API schema entries
1038
- //
1039
- // Stage E2: a DCB StateChangeSlice has a single GraphQL field but its
1040
- // command type may declare multiple constructors with different
1041
- // authorization rules. The Cognito group directive operates at field
1042
- // granularity, so we read the auth for the first constructor (matching
1043
- // the existing dcbTags convention at line 549 above). When all
1044
- // constructors share the file-level default, this is exact; when they
1045
- // differ, resolver-level enforcement still fires inside the per-slice
1046
- // handler (the file-level rule applied here is the least-restrictive
1047
- // bound).
1048
- let permissionForFirstConstructor = (
1049
- ~commandSchema: S.t<unknown>,
1050
- ~commandAuthorization: unknown => Reventless.Authorization.permission,
1051
- ): option<Reventless.Authorization.permission> => {
1052
- let names = Reventless.DcbTag.extractAllVariantNames(commandSchema->Obj.magic)
1053
- switch names->Array.get(0) {
1054
- | None => None
1055
- | Some(first) =>
1056
- let hasPayload = Reventless.DcbTag.isVariantPayloadBearing(
1057
- commandSchema->Obj.magic,
1058
- first,
1059
- )
1060
- let syntheticCmd: unknown = hasPayload ? {"TAG": first}->Obj.magic : first->Obj.magic
1061
- Some(commandAuthorization(syntheticCmd))
1062
- }
1063
- }
1049
+ // DCB-specific API schema entries. A state change slice has one field per
1050
+ // constructor, each gated by its own rule; an inbound slice has one field,
1051
+ // gated by `inboundDoorPermission`, and checks each command in `receive`.
1064
1052
  let mutationEntriesFromSlices =
1065
1053
  stateChangeSlices->Array.filterMap((module(S: StateChangeSlice.T)) => {
1066
1054
  let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
@@ -1108,14 +1096,10 @@ module Make = (
1108
1096
  inboundTranslationSlices->Array.map((module(ITS: InboundTranslationSlice.T)) => {
1109
1097
  let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=ITS.Spec.name)
1110
1098
  let fieldPermissions = Dict.make()
1111
- switch permissionForFirstConstructor(
1112
- ~commandSchema=ITS.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema,
1113
- ~commandAuthorization=command =>
1114
- ITS.Spec.commandAuthorization(command->Obj.magic)->Reventless.Authorization.named,
1115
- ) {
1116
- | Some(rule) => fieldPermissions->Dict.set(fieldName, rule)
1117
- | None => ()
1118
- }
1099
+ inboundDoorPermission(
1100
+ ITS.Spec.commandSchema,
1101
+ ITS.Spec.commandAuthorization,
1102
+ )->Option.forEach(rule => fieldPermissions->Dict.set(fieldName, rule))
1119
1103
  {
1120
1104
  ReventlessInfra.Api.fieldNames: [fieldName],
1121
1105
  commandSchema: ITS.Spec.externalInputSchema->S.castToUnknown,
@@ -1,7 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Sury from "sury";
4
- import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
4
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
5
  import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
7
6
  import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
@@ -119,6 +118,10 @@ let emptyResult = {
119
118
  eventLogEntries: emptyResult_eventLogEntries
120
119
  };
121
120
 
121
+ function inboundDoorPermission(commandSchema, commandAuthorization) {
122
+ return Plugin_Structure$ReventlessCore.inboundDoorPermission(commandSchema, cmd => Authorization$Reventless.named(commandAuthorization(cmd)));
123
+ }
124
+
122
125
  function Make(DcbEventLogStorage) {
123
126
  return DcbEventTopicPublisher => (DcbCommandTopicChannel => (DcbCommandTopicChannelAsync => (RuntimeBuilder => (HooksConfig => {
124
127
  let construct = (name, childName, apiNamePrefixOpt, onAdminApiOpt, environmentOpt, platformNameOpt, aggregateEventTopicsOpt, aggregateProducedEventsOpt, publishToAggregatesOpt, stateChangeSlices, stateViewSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, systemCallableComponentsOpt, componentRuntimeOpt, pluginStructure, opts) => {
@@ -394,7 +397,7 @@ function Make(DcbEventLogStorage) {
394
397
  let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, ITS.Spec.name);
395
398
  let registerResolver = HooksConfig.hooks.inboundMutationResolverHook;
396
399
  if (registerResolver !== undefined) {
397
- registerResolver(fieldName, ITS.Spec.externalInputSchema);
400
+ registerResolver(fieldName, ITS.Spec.externalInputSchema, inboundDoorPermission(ITS.Spec.commandSchema, ITS.Spec.commandAuthorization));
398
401
  }
399
402
  let bindReceive = HooksConfig.hooks.inboundMutationBindReceiveHook;
400
403
  if (bindReceive !== undefined) {
@@ -464,12 +467,10 @@ function Make(DcbEventLogStorage) {
464
467
  return (event, ctx) => {
465
468
  let match = event["__inboundTranslation"];
466
469
  if (match !== undefined) {
467
- let fieldName = Stdlib_Option.getOr(Stdlib_Option.flatMap(event["fieldName"], Stdlib_JSON.Decode.string), "");
468
- let args = Stdlib_Option.getOr(event["arguments"], null);
469
- let receiveFn = receivers[fieldName];
470
+ let receiveFn = receivers[event.fieldName];
470
471
  if (receiveFn !== undefined) {
471
472
  return Effect.promise(async () => {
472
- let result = await receiveFn(args);
473
+ let result = await receiveFn(event.arguments, InboundTranslationSlice_Callback$ReventlessCore.doorCaller(event.identity));
473
474
  return CommandTopic$ReventlessCore.commandOutcomeToJson(InboundTranslationSlice_Callback$ReventlessCore.receiveResultToOutcome(result));
474
475
  });
475
476
  } else {
@@ -544,18 +545,6 @@ function Make(DcbEventLogStorage) {
544
545
  RuntimeBuilder.forDcbCommandTopic(asyncDcbHandler, asyncDcbConnectFn, sliceMemoryFloor(asyncSlices), sliceTimeoutFloor(asyncSlices), asyncDcbCommandTopic);
545
546
  });
546
547
  };
547
- let permissionForFirstConstructor = (commandSchema, commandAuthorization) => {
548
- let names = DcbTag$Reventless.extractAllVariantNames(commandSchema);
549
- let first = names[0];
550
- if (first === undefined) {
551
- return;
552
- }
553
- let hasPayload = DcbTag$Reventless.isVariantPayloadBearing(commandSchema, first);
554
- let syntheticCmd = hasPayload ? ({
555
- TAG: first
556
- }) : first;
557
- return commandAuthorization(syntheticCmd);
558
- };
559
548
  let mutationEntriesFromSlices = Stdlib_Array.filterMap(stateChangeSlices, S => {
560
549
  let commandSchema = S.Spec.commandSchema;
561
550
  let fieldSpecs = Api_Naming$ReventlessCore.sliceMutationFields(apiNamePrefix, S.Spec.name, commandSchema);
@@ -585,10 +574,9 @@ function Make(DcbEventLogStorage) {
585
574
  let mutationEntriesFromInboundSlices = inboundTranslationSlices.map(ITS => {
586
575
  let fieldName = Api_Naming$ReventlessCore.sliceMutationField(name, ITS.Spec.name);
587
576
  let fieldPermissions = {};
588
- let rule = permissionForFirstConstructor(ITS.Spec.commandSchema, command => Authorization$Reventless.named(ITS.Spec.commandAuthorization(command)));
589
- if (rule !== undefined) {
577
+ Stdlib_Option.forEach(inboundDoorPermission(ITS.Spec.commandSchema, ITS.Spec.commandAuthorization), rule => {
590
578
  fieldPermissions[fieldName] = rule;
591
- }
579
+ });
592
580
  return {
593
581
  fieldNames: [fieldName],
594
582
  commandSchema: ITS.Spec.externalInputSchema,
@@ -691,6 +679,7 @@ export {
691
679
  variantTag,
692
680
  mergedEventSchema,
693
681
  emptyResult,
682
+ inboundDoorPermission,
694
683
  Make,
695
684
  }
696
685
  /* log Not a pure module */
@@ -81,8 +81,8 @@ module Make = (
81
81
  ->Pulumi.Output.all2
82
82
  ->Pulumi.Output.apply(((queryDbOps, publishJsonsFn)) => {
83
83
  let ops: InboundTranslationSlice.operations = {
84
- receive: async inputJson => {
85
- let result = await Callback.receive(publishJsonsFn, inputJson)
84
+ receive: async (inputJson, ~caller=?) => {
85
+ let result = await Callback.receive(publishJsonsFn, inputJson, ~caller?)
86
86
  await syncToQueryDb(queryDbOps, result->InboundTranslationSlice_Callback.requestIdOf)
87
87
  result
88
88
  },
@@ -55,8 +55,8 @@ function Make(QueryDbStorage) {
55
55
  let publishJsonsFn = param[1];
56
56
  let queryDbOps = param[0];
57
57
  return {
58
- receive: async inputJson => {
59
- let result = await Callback.receive(publishJsonsFn, inputJson);
58
+ receive: async (inputJson, caller) => {
59
+ let result = await Callback.receive(publishJsonsFn, inputJson, caller);
60
60
  await syncToQueryDb(queryDbOps, InboundTranslationSlice_Callback$ReventlessCore.requestIdOf(result));
61
61
  return result;
62
62
  }