@reventlessdev/reventless-gwt 1.0.0-alpha.100

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 (184) hide show
  1. package/CHANGELOG.md +916 -0
  2. package/LICENSE +202 -0
  3. package/bin/reventless-dev.mjs +21 -0
  4. package/package.json +54 -0
  5. package/rescript.json +36 -0
  6. package/src/.gwtignore +10 -0
  7. package/src/Automation_GWT.res +207 -0
  8. package/src/Automation_GWT.res.mjs +160 -0
  9. package/src/Behavior_GWT.res +569 -0
  10. package/src/Behavior_GWT.res.mjs +523 -0
  11. package/src/Bind.res +29 -0
  12. package/src/Bind.res.mjs +22 -0
  13. package/src/BuildClassifier.res +147 -0
  14. package/src/BuildClassifier.res.mjs +136 -0
  15. package/src/Cancellation.res +37 -0
  16. package/src/Cancellation.res.mjs +46 -0
  17. package/src/ChildProcess.res +102 -0
  18. package/src/ChildProcess.res.mjs +107 -0
  19. package/src/Cli.res +905 -0
  20. package/src/Cli.res.mjs +952 -0
  21. package/src/Collector.res +212 -0
  22. package/src/Collector.res.mjs +218 -0
  23. package/src/ComponentMeta.res +77 -0
  24. package/src/ComponentMeta.res.mjs +91 -0
  25. package/src/ComponentScan.res +103 -0
  26. package/src/ComponentScan.res.mjs +105 -0
  27. package/src/Delegate_GWT.res +303 -0
  28. package/src/Delegate_GWT.res.mjs +372 -0
  29. package/src/Diff.res +91 -0
  30. package/src/Diff.res.mjs +98 -0
  31. package/src/Discovery.res +105 -0
  32. package/src/Discovery.res.mjs +99 -0
  33. package/src/DomainDeadCode.res +98 -0
  34. package/src/DomainDeadCode.res.mjs +72 -0
  35. package/src/DomainGraph.res +0 -0
  36. package/src/DomainGraph.res.mjs +0 -0
  37. package/src/EventMapping_GWT.res +29 -0
  38. package/src/EventMapping_GWT.res.mjs +44 -0
  39. package/src/Filter.res +45 -0
  40. package/src/Filter.res.mjs +47 -0
  41. package/src/Flow_GWT.res +664 -0
  42. package/src/Flow_GWT.res.mjs +604 -0
  43. package/src/FormatterHuman.res +109 -0
  44. package/src/FormatterHuman.res.mjs +115 -0
  45. package/src/FormatterJson.res +264 -0
  46. package/src/FormatterJson.res.mjs +322 -0
  47. package/src/FormatterJunit.res +93 -0
  48. package/src/FormatterJunit.res.mjs +90 -0
  49. package/src/FormatterTap.res +100 -0
  50. package/src/FormatterTap.res.mjs +131 -0
  51. package/src/FormatterVsCode.res +352 -0
  52. package/src/FormatterVsCode.res.mjs +546 -0
  53. package/src/Hint.res +119 -0
  54. package/src/Hint.res.mjs +124 -0
  55. package/src/InboundTranslation_GWT.res +83 -0
  56. package/src/InboundTranslation_GWT.res.mjs +94 -0
  57. package/src/JestBind.res +70 -0
  58. package/src/JestBind.res.mjs +63 -0
  59. package/src/Loader.res +24 -0
  60. package/src/Loader.res.mjs +23 -0
  61. package/src/LocalHost.res +119 -0
  62. package/src/LocalHost.res.mjs +99 -0
  63. package/src/Mapping_GWT.res +474 -0
  64. package/src/Mapping_GWT.res.mjs +322 -0
  65. package/src/MismatchRender.res +72 -0
  66. package/src/MismatchRender.res.mjs +127 -0
  67. package/src/MultiSourceProjection_GWT.res +316 -0
  68. package/src/MultiSourceProjection_GWT.res.mjs +354 -0
  69. package/src/OutboundTranslation_GWT.res +238 -0
  70. package/src/OutboundTranslation_GWT.res.mjs +186 -0
  71. package/src/Outcome.res +97 -0
  72. package/src/Outcome.res.mjs +93 -0
  73. package/src/PackageScan.res +80 -0
  74. package/src/PackageScan.res.mjs +81 -0
  75. package/src/PlatformRunner.res +181 -0
  76. package/src/PlatformRunner.res.mjs +176 -0
  77. package/src/PlatformScan.res +142 -0
  78. package/src/PlatformScan.res.mjs +132 -0
  79. package/src/ProcessManager.res +135 -0
  80. package/src/ProcessManager.res.mjs +119 -0
  81. package/src/Projection_GWT.res +312 -0
  82. package/src/Projection_GWT.res.mjs +332 -0
  83. package/src/Query_GWT.res +379 -0
  84. package/src/Query_GWT.res.mjs +367 -0
  85. package/src/RenderRescript.res +125 -0
  86. package/src/RenderRescript.res.mjs +141 -0
  87. package/src/RunWorker.res +24 -0
  88. package/src/RunWorker.res.mjs +16 -0
  89. package/src/RunnerTypes.res +68 -0
  90. package/src/RunnerTypes.res.mjs +60 -0
  91. package/src/SideEffect_GWT.res +157 -0
  92. package/src/SideEffect_GWT.res.mjs +113 -0
  93. package/src/StateChange/ExternalAddCategorySlice.res +25 -0
  94. package/src/StateChange/ExternalAddCategorySlice.res.mjs +34 -0
  95. package/src/StateChange/ExternalAddCategorySlice_Behavior.res +19 -0
  96. package/src/StateChange/ExternalAddCategorySlice_Behavior.res.mjs +46 -0
  97. package/src/StateChange/WithFixtures.res +22 -0
  98. package/src/StateChange/WithFixtures.res.mjs +31 -0
  99. package/src/StateChange/WithFixtures_Behavior.res +17 -0
  100. package/src/StateChange/WithFixtures_Behavior.res.mjs +37 -0
  101. package/src/StateChange/WithManualOpen.res +23 -0
  102. package/src/StateChange/WithManualOpen.res.mjs +31 -0
  103. package/src/StateChange/WithManualOpen_Behavior.res +17 -0
  104. package/src/StateChange/WithManualOpen_Behavior.res.mjs +37 -0
  105. package/src/StubRuntime.res +54 -0
  106. package/src/StubRuntime.res.mjs +55 -0
  107. package/src/TestFixtures.res +16 -0
  108. package/src/TestFixtures.res.mjs +34 -0
  109. package/src/Watch.res +108 -0
  110. package/src/Watch.res.mjs +85 -0
  111. package/src/WatcherProbe.res +56 -0
  112. package/src/WatcherProbe.res.mjs +59 -0
  113. package/src/Worker.res +24 -0
  114. package/src/Worker.res.mjs +9 -0
  115. package/tests/AutomationGwtTest.res +71 -0
  116. package/tests/AutomationGwtTest.res.mjs +189 -0
  117. package/tests/BuildClassifierTest.res +133 -0
  118. package/tests/BuildClassifierTest.res.mjs +176 -0
  119. package/tests/CliRunEntryTest.res +57 -0
  120. package/tests/CliRunEntryTest.res.mjs +65 -0
  121. package/tests/CliWatchScopeTest.res +62 -0
  122. package/tests/CliWatchScopeTest.res.mjs +95 -0
  123. package/tests/ComponentMetaTest.res +67 -0
  124. package/tests/ComponentMetaTest.res.mjs +71 -0
  125. package/tests/DelegateGwtTest.res +487 -0
  126. package/tests/DelegateGwtTest.res.mjs +891 -0
  127. package/tests/DiscoveryTest.res +42 -0
  128. package/tests/DiscoveryTest.res.mjs +43 -0
  129. package/tests/DomainDeadCodeTest.res +119 -0
  130. package/tests/DomainDeadCodeTest.res.mjs +158 -0
  131. package/tests/DomainGraphTest.res +448 -0
  132. package/tests/DomainGraphTest.res.mjs +505 -0
  133. package/tests/FlowAggregateGwtTest.res +171 -0
  134. package/tests/FlowAggregateGwtTest.res.mjs +273 -0
  135. package/tests/FlowCrossPluginGwtTest.res +340 -0
  136. package/tests/FlowCrossPluginGwtTest.res.mjs +798 -0
  137. package/tests/FlowGwtTest.res +222 -0
  138. package/tests/FlowGwtTest.res.mjs +433 -0
  139. package/tests/FormatterGoldenTest.res +125 -0
  140. package/tests/FormatterGoldenTest.res.mjs +162 -0
  141. package/tests/FormatterVsCodeMessageTest.res +41 -0
  142. package/tests/FormatterVsCodeMessageTest.res.mjs +42 -0
  143. package/tests/InboundTranslationGwtTest.res +34 -0
  144. package/tests/InboundTranslationGwtTest.res.mjs +99 -0
  145. package/tests/LocalHostIntegration.res +70 -0
  146. package/tests/LocalHostIntegration.res.mjs +64 -0
  147. package/tests/LocalHostTest.res +71 -0
  148. package/tests/LocalHostTest.res.mjs +63 -0
  149. package/tests/MappingGwtTest.res +350 -0
  150. package/tests/MappingGwtTest.res.mjs +596 -0
  151. package/tests/OutboundTranslationGwtTest.res +59 -0
  152. package/tests/OutboundTranslationGwtTest.res.mjs +139 -0
  153. package/tests/PlatformRunnerTest.res +66 -0
  154. package/tests/PlatformRunnerTest.res.mjs +85 -0
  155. package/tests/PlatformScanTest.res +61 -0
  156. package/tests/PlatformScanTest.res.mjs +56 -0
  157. package/tests/QueryGwtTest.res +161 -0
  158. package/tests/QueryGwtTest.res.mjs +314 -0
  159. package/tests/QueryResolverGwtTest.res +71 -0
  160. package/tests/QueryResolverGwtTest.res.mjs +136 -0
  161. package/tests/RunnerUnitTest.res +219 -0
  162. package/tests/RunnerUnitTest.res.mjs +244 -0
  163. package/tests/SideEffect/FakeOrderNotification.res +23 -0
  164. package/tests/SideEffect/FakeOrderNotification.res.mjs +36 -0
  165. package/tests/SideEffect/FakeOrderNotificationGwtTest.res +35 -0
  166. package/tests/SideEffect/FakeOrderNotificationGwtTest.res.mjs +73 -0
  167. package/tests/SideEffect/MockEmail.res +18 -0
  168. package/tests/SideEffect/MockEmail.res.mjs +47 -0
  169. package/tests/StateChange/ExternalAddCategorySlice_GWT.res +27 -0
  170. package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +70 -0
  171. package/tests/StateChange/WithFixtures_Fixtures.res +9 -0
  172. package/tests/StateChange/WithFixtures_Fixtures.res.mjs +20 -0
  173. package/tests/StateChange/WithFixtures_GWT.res +23 -0
  174. package/tests/StateChange/WithFixtures_GWT.res.mjs +56 -0
  175. package/tests/StateChange/WithManualOpen_Fixtures.res +7 -0
  176. package/tests/StateChange/WithManualOpen_Fixtures.res.mjs +20 -0
  177. package/tests/StateChange/WithManualOpen_GWT.res +17 -0
  178. package/tests/StateChange/WithManualOpen_GWT.res.mjs +56 -0
  179. package/tests/StateChangeSliceGwtTest.res +160 -0
  180. package/tests/StateChangeSliceGwtTest.res.mjs +280 -0
  181. package/tests/StateViewSliceGwtTest.res +64 -0
  182. package/tests/StateViewSliceGwtTest.res.mjs +161 -0
  183. package/tests/WatchDebounceTest.res +106 -0
  184. package/tests/WatchDebounceTest.res.mjs +128 -0
@@ -0,0 +1,105 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Nodefs from "node:fs";
4
+ import * as Nodepath from "node:path";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Promises from "node:fs/promises";
7
+ import * as ComponentMeta$ReventlessGwt from "./ComponentMeta.res.mjs";
8
+
9
+ let ignoreNames = [
10
+ "node_modules",
11
+ ".git",
12
+ "dist",
13
+ "lib",
14
+ ".history"
15
+ ];
16
+
17
+ function shouldIgnore(name) {
18
+ return ignoreNames.includes(name);
19
+ }
20
+
21
+ let gwtIgnoreFile = ".gwtignore";
22
+
23
+ function isPruned(entries) {
24
+ return entries.some(e => e.name === gwtIgnoreFile);
25
+ }
26
+
27
+ function isSrcResFile(name) {
28
+ return name.endsWith(".res");
29
+ }
30
+
31
+ async function walk(dir, acc) {
32
+ let entries;
33
+ try {
34
+ entries = await Promises.readdir(dir, {
35
+ withFileTypes: true
36
+ });
37
+ } catch (exn) {
38
+ entries = [];
39
+ }
40
+ if (isPruned(entries)) {
41
+ return acc;
42
+ }
43
+ let found = acc;
44
+ for (let i = 0, i_finish = entries.length; i < i_finish; ++i) {
45
+ let entry = entries[i];
46
+ if (!ignoreNames.includes(entry.name)) {
47
+ let full = Nodepath.join(dir, entry.name);
48
+ if (entry.isDirectory()) {
49
+ let nested = await walk(full, []);
50
+ found = found.concat(nested);
51
+ } else if (entry.isFile() && entry.name.endsWith(".res")) {
52
+ found = found.concat([full]);
53
+ }
54
+ }
55
+ }
56
+ return found;
57
+ }
58
+
59
+ async function scan(pkgDirs) {
60
+ let order = [];
61
+ let filesByKey = {};
62
+ for (let i = 0, i_finish = pkgDirs.length; i < i_finish; ++i) {
63
+ let dir = pkgDirs[i];
64
+ let srcDir = Nodepath.join(dir, "src");
65
+ if (Nodefs.existsSync(srcDir)) {
66
+ let files = await walk(srcDir, []);
67
+ files.forEach(file => {
68
+ let c = ComponentMeta$ReventlessGwt.componentOfSrcFile(file);
69
+ if (c === undefined) {
70
+ return;
71
+ }
72
+ let key = dir + "::" + c.kind + "::" + c.name;
73
+ let existing = filesByKey[key];
74
+ if (existing !== undefined) {
75
+ filesByKey[key] = existing.concat([file]);
76
+ } else {
77
+ filesByKey[key] = [file];
78
+ order.push({
79
+ dir: dir,
80
+ kind: c.kind,
81
+ name: c.name,
82
+ key: key
83
+ });
84
+ }
85
+ });
86
+ }
87
+ }
88
+ return order.map(o => ({
89
+ dir: o.dir,
90
+ kind: o.kind,
91
+ name: o.name,
92
+ files: Stdlib_Option.getOr(filesByKey[o.key], [])
93
+ }));
94
+ }
95
+
96
+ export {
97
+ ignoreNames,
98
+ shouldIgnore,
99
+ gwtIgnoreFile,
100
+ isPruned,
101
+ isSrcResFile,
102
+ walk,
103
+ scan,
104
+ }
105
+ /* node:fs Not a pure module */
@@ -0,0 +1,303 @@
1
+ open ReventlessCore
2
+
3
+ // Delegate_GWT — GWT for ExtensionPoint mappings and Extension delegates.
4
+ //
5
+ // Both are pure input → published-actions translations and form the one
6
+ // cross-plugin behavioural seam (per `.claude/rules/app-developer.md`). Each
7
+ // mapping has TWO directions, and each adapter drives both:
8
+ //
9
+ // * an ExtensionPoint mapping turns an internal `Delegate` event into public
10
+ // extension-point EVENTS (`mapOutgoingEvent`), and an incoming protocol
11
+ // COMMAND into a wrapped-aggregate command (`mapIncomingCommand`);
12
+ // * an Extension delegate turns an incoming extension-point EVENT into delegate
13
+ // COMMANDS (`mapIncomingEvent`), and an internal `Delegate` event into an
14
+ // outgoing protocol COMMAND (`mapOutgoingEvent`).
15
+ //
16
+ // The stimulus you inject names what arrives at the mapping, NOT what it emits:
17
+ // * `whenDelegateEvent` — an internal delegate event occurs (both adapters'
18
+ // `mapOutgoingEvent`);
19
+ // * `whenIncomingEvent` — a protocol event arrives (Extension `mapIncomingEvent`);
20
+ // * `whenIncomingCommand`— a protocol command arrives (EP `mapIncomingCommand`).
21
+ //
22
+ // This DSL drives the user-level mapping function with the inert `StubRuntime`
23
+ // handles and asserts the SET of published actions — including one-to-many
24
+ // fan-out (e.g. `OrderPlaced{productIds:[…]}` → N × `ItemOrdered`).
25
+ //
26
+ // `FromExtensionPoint(M)` / `FromExtension(M)` mirror the From* adapter pattern
27
+ // of `Mapping_GWT` / `Query_GWT`: one comparison core per direction.
28
+ //
29
+ // See `docs/plans/done/gwt-flow-and-extension-test-kinds.md` Phase 1.
30
+
31
+ module EPMapping = ReventlessInfra.ExtensionPointMapping
32
+ module ExtMapping = ReventlessInfra.ExtensionMapping
33
+
34
+ // A published action normalised for comparison: a routing target plus the
35
+ // encoded payload. EP events are keyed by entity id; delegate commands by the
36
+ // component (slice/aggregate) or extension-point they route to.
37
+ let encPublished = (~target: string, payload: JSON.t): JSON.t => {
38
+ let d = Dict.make()
39
+ d->Dict.set("target", JSON.Encode.string(target))
40
+ d->Dict.set("payload", payload)
41
+ JSON.Encode.object(d)
42
+ }
43
+
44
+ // -- Comparison core ---------------------------------------------------------
45
+
46
+ module type Delegate = {
47
+ let name: string
48
+ type inbound
49
+ // Drive the underlying mapping with StubRuntime, returning the published
50
+ // actions already normalised to JSON.
51
+ let run: inbound => promise<array<JSON.t>>
52
+ }
53
+
54
+ module type T = {
55
+ type inbound
56
+
57
+ let describe: (string, unit => unit) => unit
58
+ let test: (string, ~timeout: int=?, unit => promise<Outcome.outcome>) => unit
59
+
60
+ // Feed the stimulus into the mapping; returns the normalised published actions.
61
+ // The adapters re-export this under direction-specific names (whenDelegateEvent
62
+ // / whenIncomingEvent / whenIncomingCommand).
63
+ let whenInput: inbound => promise<array<JSON.t>>
64
+ let thenPublishedJson: (promise<array<JSON.t>>, array<JSON.t>) => promise<Outcome.outcome>
65
+ let thenPublishesNothing: promise<array<JSON.t>> => promise<Outcome.outcome>
66
+ // Assert the SET of HANDLED DIRECTIVES (HandleDirective actions, encoded by the
67
+ // EP/Extension directive schema). Disjoint from thenPublishedJson — published
68
+ // events/commands and handled directives are separate channels.
69
+ let thenHandlesDirectivesJson: (promise<array<JSON.t>>, array<JSON.t>) => promise<Outcome.outcome>
70
+ }
71
+
72
+ module Make = (D: Delegate): (T with type inbound = D.inbound) => {
73
+ type inbound = D.inbound
74
+
75
+ S.enableJson()
76
+
77
+ let describe = JestBind.describe
78
+ let test = (name, ~timeout=?, body) =>
79
+ JestBind.testPromise(~slice=D.name, name, ~timeout?, body)
80
+
81
+ let whenInput = ev => D.run(ev)
82
+
83
+ // Published actions form a SET — fan-out order is non-deterministic, so the
84
+ // comparison sorts by stringified JSON before comparing.
85
+ let sortJson = (arr: array<JSON.t>) =>
86
+ arr->Array.toSorted((a, b) => String.compare(JSON.stringify(a), JSON.stringify(b)))
87
+
88
+ // Handled directives ride the same normalised array tagged `target:"directive"`
89
+ // so a single mapping run carries both channels; the publish/directive verbs
90
+ // each project to their own. (Published actions never use that target — EP/Ext
91
+ // events and commands route to an id, a component name, or `<ep>:<id>`.)
92
+ let targetOf = (j: JSON.t): string =>
93
+ j
94
+ ->JSON.Decode.object
95
+ ->Option.flatMap(d => d->Dict.get("target"))
96
+ ->Option.flatMap(JSON.Decode.string)
97
+ ->Option.getOr("")
98
+ let dropDirectives = arr => arr->Array.filter(j => targetOf(j) != "directive")
99
+ let onlyDirectives = arr => arr->Array.filter(j => targetOf(j) == "directive")
100
+
101
+ let thenPublishedJson = async (actionsP, expected) => {
102
+ let actual = (await actionsP)->dropDirectives
103
+ if sortJson(actual) == sortJson(expected) {
104
+ Outcome.pass
105
+ } else {
106
+ Outcome.fail(PublishedActionsMismatch({expected, actual}))
107
+ }
108
+ }
109
+
110
+ let thenPublishesNothing = actionsP => thenPublishedJson(actionsP, [])
111
+
112
+ let thenHandlesDirectivesJson = async (actionsP, expected) => {
113
+ let actual = (await actionsP)->onlyDirectives
114
+ if sortJson(actual) == sortJson(expected) {
115
+ Outcome.pass
116
+ } else {
117
+ Outcome.fail(PublishedActionsMismatch({expected, actual}))
118
+ }
119
+ }
120
+ }
121
+
122
+ // -- FromExtensionPoint: an EP mapping (both directions) ---------------------
123
+
124
+ module FromExtensionPoint = (M: EPMapping.Mapping) => {
125
+ let encEvent = (e: M.ExtensionPoint.event) => e->Message.encode(M.ExtensionPoint.eventSchema)
126
+ let encCommand = (c: M.Delegate.command) => c->Message.encode(M.Delegate.commandSchema)
127
+ let encDirective = (d: M.ExtensionPoint.directive) =>
128
+ encPublished(~target="directive", d->Message.encode(M.ExtensionPoint.directiveSchema))
129
+
130
+ // Internal delegate event → outgoing extension-point events (`mapOutgoingEvent`).
131
+ module EvCore = Make({
132
+ let name = M.ExtensionPoint.name
133
+ type inbound = M.Delegate.event
134
+ let run = async (event: M.Delegate.event) =>
135
+ switch M.mapOutgoingEvent {
136
+ | None => []
137
+ | Some(f) =>
138
+ let nested =
139
+ await f("gwt-id", event, StubRuntime.meta, StubRuntime.queryEngine)
140
+ ->Array.map(async action =>
141
+ switch action {
142
+ | EPMapping.PublishEvent(id, ev) => [encPublished(~target=id, encEvent(ev))]
143
+ | EPMapping.PublishEventAsync(p) =>
144
+ let (id, ev) = await p
145
+ [encPublished(~target=id, encEvent(ev))]
146
+ | EPMapping.HandleDirective(_, dir) => [encDirective(dir)]
147
+ }
148
+ )
149
+ ->Promise.all
150
+ nested->Array.flat
151
+ }
152
+ })
153
+
154
+ // Incoming extension-point command → wrapped-aggregate commands (`mapIncomingCommand`).
155
+ module CmdCore = Make({
156
+ let name = M.ExtensionPoint.name
157
+ type inbound = M.ExtensionPoint.command
158
+ let run = async (command: M.ExtensionPoint.command) =>
159
+ M.mapIncomingCommand("gwt-id", command, StubRuntime.meta)->Array.filterMap(action =>
160
+ switch action {
161
+ | EPMapping.PublishCommand(id, cmd) => Some(encPublished(~target=id, encCommand(cmd)))
162
+ | EPMapping.HandleDirective(_, dir) => Some(encDirective(dir))
163
+ }
164
+ )
165
+ })
166
+
167
+ type inbound = M.Delegate.event
168
+ let describe = EvCore.describe
169
+ let test = EvCore.test
170
+ // `thenPublishesNothing` is direction-agnostic (compares to []), usable after
171
+ // either `whenDelegateEvent` or `whenIncomingCommand`.
172
+ let thenPublishesNothing = EvCore.thenPublishesNothing
173
+
174
+ // Delegate event → outgoing protocol events.
175
+ let whenDelegateEvent = EvCore.whenInput
176
+ let thenPublishesEvent = (actionsP, id, event: M.ExtensionPoint.event) =>
177
+ EvCore.thenPublishedJson(actionsP, [encPublished(~target=id, encEvent(event))])
178
+ let thenPublishesEvents = (actionsP, pairs: array<(string, M.ExtensionPoint.event)>) =>
179
+ EvCore.thenPublishedJson(
180
+ actionsP,
181
+ pairs->Array.map(((id, ev)) => encPublished(~target=id, encEvent(ev))),
182
+ )
183
+
184
+ // Incoming protocol command → wrapped-aggregate commands.
185
+ let whenIncomingCommand = CmdCore.whenInput
186
+ let thenPublishesCommand = (actionsP, id, cmd: M.Delegate.command) =>
187
+ CmdCore.thenPublishedJson(actionsP, [encPublished(~target=id, encCommand(cmd))])
188
+ let thenPublishesCommands = (actionsP, pairs: array<(string, M.Delegate.command)>) =>
189
+ CmdCore.thenPublishedJson(
190
+ actionsP,
191
+ pairs->Array.map(((id, cmd)) => encPublished(~target=id, encCommand(cmd))),
192
+ )
193
+
194
+ // Handled directives — usable after either `when`; both cores tag directives on
195
+ // the same channel, so this projects them out regardless of direction.
196
+ let thenHandlesDirective = (actionsP, directive: M.ExtensionPoint.directive) =>
197
+ EvCore.thenHandlesDirectivesJson(actionsP, [encDirective(directive)])
198
+ let thenHandlesDirectives = (actionsP, directives: array<M.ExtensionPoint.directive>) =>
199
+ EvCore.thenHandlesDirectivesJson(actionsP, directives->Array.map(encDirective))
200
+ let thenHandlesNoDirective = actionsP => EvCore.thenHandlesDirectivesJson(actionsP, [])
201
+ }
202
+
203
+ // -- FromExtension: an Extension delegate (both directions) ------------------
204
+
205
+ module FromExtension = (M: ExtMapping.Mapping) => {
206
+ let encCmd = (c: M.Delegate.command) => c->Message.encode(M.Delegate.commandSchema)
207
+ let encEpCmd = (c: M.ExtensionPoint.command) => c->Message.encode(M.ExtensionPoint.commandSchema)
208
+ let encDirective = (d: M.ExtensionPoint.directive) =>
209
+ encPublished(~target="directive", d->Message.encode(M.ExtensionPoint.directiveSchema))
210
+
211
+ // Incoming extension-point event → delegate commands (`mapIncomingEvent`).
212
+ module EvCore = Make({
213
+ let name = M.Delegate.name
214
+ type inbound = M.ExtensionPoint.event
215
+ let run = async (event: M.ExtensionPoint.event) => {
216
+ let nested =
217
+ await M.mapIncomingEvent(
218
+ "gwt-id",
219
+ event,
220
+ StubRuntime.meta,
221
+ StubRuntime.pluginDefinition,
222
+ StubRuntime.queryEngine,
223
+ )
224
+ ->Array.map(async action =>
225
+ switch action {
226
+ | ExtMapping.PublishStateChangeSliceCommand(cmd) => [
227
+ encPublished(~target=M.Delegate.name, encCmd(cmd)),
228
+ ]
229
+ | ExtMapping.PublishStateChangeSliceCommandAsync(p) =>
230
+ let cmd = await p
231
+ [encPublished(~target=M.Delegate.name, encCmd(cmd))]
232
+ | ExtMapping.PublishStateChangeSliceCommandsAsync(p) =>
233
+ (await p)->Array.map(cmd => encPublished(~target=M.Delegate.name, encCmd(cmd)))
234
+ | ExtMapping.PublishAggregateCommand(id, cmd) => [encPublished(~target=id, encCmd(cmd))]
235
+ | ExtMapping.PublishAggregateCommandAsync(p) =>
236
+ let (id, cmd) = await p
237
+ [encPublished(~target=id, encCmd(cmd))]
238
+ | ExtMapping.PublishAggregateCommandsAsync(p) =>
239
+ (await p)->Array.map(((id, cmd)) => encPublished(~target=id, encCmd(cmd)))
240
+ | ExtMapping.PublishExtensionPointCommand(id, cmd) => [
241
+ encPublished(~target=id, encEpCmd(cmd)),
242
+ ]
243
+ | ExtMapping.ForwardCommand({extensionPointName, id, commandJson}) => [
244
+ encPublished(~target=`${extensionPointName}:${id}`, commandJson),
245
+ ]
246
+ | ExtMapping.HandleDirective(_, dir) => [encDirective(dir)]
247
+ }
248
+ )
249
+ ->Promise.all
250
+ nested->Array.flat
251
+ }
252
+ })
253
+
254
+ // Internal delegate event → outgoing protocol commands (`mapOutgoingEvent`, optional).
255
+ module OutCore = Make({
256
+ let name = M.Delegate.name
257
+ type inbound = M.Delegate.event
258
+ let run = async (event: M.Delegate.event) =>
259
+ switch M.mapOutgoingEvent {
260
+ | None => []
261
+ | Some(f) =>
262
+ f("gwt-id", event, StubRuntime.meta, StubRuntime.pluginDefinition)->Array.filterMap(action =>
263
+ switch action {
264
+ | ExtMapping.PublishExtensionPointCommand(id, cmd) =>
265
+ Some(encPublished(~target=id, encEpCmd(cmd)))
266
+ | ExtMapping.ForwardCommand({extensionPointName, id, commandJson}) =>
267
+ Some(encPublished(~target=`${extensionPointName}:${id}`, commandJson))
268
+ | ExtMapping.HandleDirective(_, dir) => Some(encDirective(dir))
269
+ }
270
+ )
271
+ }
272
+ })
273
+
274
+ type inbound = M.ExtensionPoint.event
275
+ let describe = EvCore.describe
276
+ let test = EvCore.test
277
+ let thenPublishesNothing = EvCore.thenPublishesNothing
278
+
279
+ // Incoming protocol event → delegate commands.
280
+ let whenIncomingEvent = EvCore.whenInput
281
+ let thenPublishesCommand = (actionsP, cmd: M.Delegate.command) =>
282
+ EvCore.thenPublishedJson(actionsP, [encPublished(~target=M.Delegate.name, encCmd(cmd))])
283
+ let thenPublishesCommands = (actionsP, cmds: array<M.Delegate.command>) =>
284
+ EvCore.thenPublishedJson(
285
+ actionsP,
286
+ cmds->Array.map(cmd => encPublished(~target=M.Delegate.name, encCmd(cmd))),
287
+ )
288
+ let thenPublishesAggregateCommand = (actionsP, id, cmd: M.Delegate.command) =>
289
+ EvCore.thenPublishedJson(actionsP, [encPublished(~target=id, encCmd(cmd))])
290
+
291
+ // Internal delegate event → outgoing protocol commands.
292
+ let whenDelegateEvent = OutCore.whenInput
293
+ let thenPublishesExtensionPointCommand = (actionsP, id, cmd: M.ExtensionPoint.command) =>
294
+ OutCore.thenPublishedJson(actionsP, [encPublished(~target=id, encEpCmd(cmd))])
295
+
296
+ // Handled directives — usable after either `when` (both cores tag directives on
297
+ // the same channel).
298
+ let thenHandlesDirective = (actionsP, directive: M.ExtensionPoint.directive) =>
299
+ EvCore.thenHandlesDirectivesJson(actionsP, [encDirective(directive)])
300
+ let thenHandlesDirectives = (actionsP, directives: array<M.ExtensionPoint.directive>) =>
301
+ EvCore.thenHandlesDirectivesJson(actionsP, directives->Array.map(encDirective))
302
+ let thenHandlesNoDirective = actionsP => EvCore.thenHandlesDirectivesJson(actionsP, [])
303
+ }