@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,238 @@
1
+ open ReventlessCore
2
+
3
+ // Minimal inline spec for OutboundTranslationSlice. `translate` is async and
4
+ // supplied at test-time via `whenTranslateMocked` so the external service is
5
+ // never hit. The spec's own `translate` is not part of the GWT — it's
6
+ // exercised only through the runtime tests.
7
+ module type SliceSpec = {
8
+ let name: string
9
+
10
+ @schema
11
+ type consumedEvent
12
+
13
+ @schema
14
+ type outboundItem
15
+
16
+ @schema
17
+ type inboundCommand
18
+
19
+ let collect: consumedEvent => array<(string, outboundItem)>
20
+ }
21
+
22
+ // Status enum reflecting how the slice's runtime labels a TODO after a
23
+ // translate call. A successful translate (Ok) marks the item #Completed;
24
+ // a failure (Error) leaves it #Pending for retry up to `maxRetries`.
25
+ type todoStatus = [#Completed | #Pending]
26
+
27
+ module type T = {
28
+ module Spec: SliceSpec
29
+
30
+ let describe: (string, unit => unit) => unit
31
+ let test: (string, ~timeout: int=?, unit => promise<Outcome.outcome>) => unit
32
+ // Sync companion for the `collect` leg, whose combinators return
33
+ // `Outcome.outcome` directly (no Promise). The async `test` works for
34
+ // collect bodies too, but only after a manual `Promise.resolve` wrapper.
35
+ let testSync: (string, unit => Outcome.outcome) => unit
36
+
37
+ type translateResult =
38
+ result<option<(string, Spec.inboundCommand)>, string>
39
+
40
+ // The state the pipeline carries after a translate attempt. `retries` counts
41
+ // the number of failed re-attempts (0 for a single `whenTranslateMocked`).
42
+ type attempt = {
43
+ id: string,
44
+ item: Spec.outboundItem,
45
+ result: translateResult,
46
+ retries: int,
47
+ }
48
+
49
+ // Unit — collect
50
+ let givenEvent: Spec.consumedEvent => Spec.consumedEvent
51
+ let whenCollect: Spec.consumedEvent => array<(string, Spec.outboundItem)>
52
+ let thenTodos: (
53
+ array<(string, Spec.outboundItem)>,
54
+ array<(string, Spec.outboundItem)>,
55
+ ) => Outcome.outcome
56
+
57
+ // Unit — translate
58
+ let givenTodo: (string, Spec.outboundItem) => (string, Spec.outboundItem)
59
+ let whenTranslateMocked: (
60
+ (string, Spec.outboundItem),
61
+ (string, Spec.outboundItem) => promise<translateResult>,
62
+ ) => promise<attempt>
63
+ // Re-invokes the mock on each `Error` up to `maxRetries` times (or until it
64
+ // returns `Ok`), tracking how many retries were spent — the real counter
65
+ // `thenRetryRecorded` asserts against.
66
+ let whenTranslateRetrying: (
67
+ (string, Spec.outboundItem),
68
+ ~maxRetries: int,
69
+ (string, Spec.outboundItem) => promise<translateResult>,
70
+ ) => promise<attempt>
71
+ let thenCommand: (
72
+ promise<attempt>,
73
+ string,
74
+ Spec.inboundCommand,
75
+ ) => promise<Outcome.outcome>
76
+ let thenNoCommand: promise<attempt> => promise<Outcome.outcome>
77
+ let thenRetryRecorded: (promise<attempt>, int) => promise<Outcome.outcome>
78
+ let thenTodoStatus: (promise<attempt>, string, todoStatus) => promise<Outcome.outcome>
79
+ }
80
+
81
+ module Make = (Spec: SliceSpec): (T with module Spec = Spec) => {
82
+ module Spec = Spec
83
+
84
+ S.enableJson()
85
+
86
+ let describe = JestBind.describe
87
+ let test = (name, ~timeout=?, body) =>
88
+ JestBind.testPromise(~slice=Spec.name, name, ~timeout?, body)
89
+ let testSync = (name, body) => JestBind.test(~slice=Spec.name, name, body)
90
+
91
+ type translateResult =
92
+ result<option<(string, Spec.inboundCommand)>, string>
93
+
94
+ type attempt = {
95
+ id: string,
96
+ item: Spec.outboundItem,
97
+ result: translateResult,
98
+ retries: int,
99
+ }
100
+
101
+ let encItem = (i: Spec.outboundItem) => i->Message.encode(Spec.outboundItemSchema)
102
+ let encItems = (arr: array<(string, Spec.outboundItem)>) =>
103
+ arr->Array.map(((id, i)) => (id, encItem(i)))
104
+ let encInbound = (c: Spec.inboundCommand) =>
105
+ c->Message.encode(Spec.inboundCommandSchema)
106
+
107
+ // Unit: collect
108
+ let givenEvent = e => e
109
+ let whenCollect = e => e->Spec.collect
110
+ let thenTodos = (actual, expected) =>
111
+ if actual == expected {
112
+ Outcome.pass
113
+ } else {
114
+ Outcome.fail(
115
+ TodoMismatch({
116
+ expected: encItems(expected),
117
+ actual: encItems(actual),
118
+ }),
119
+ )
120
+ }
121
+
122
+ // Unit: translate
123
+ let givenTodo = (id, item) => (id, item)
124
+ let whenTranslateMocked = async ((id, item), mock) => {
125
+ let result = await mock(id, item)
126
+ {id, item, result, retries: 0}
127
+ }
128
+
129
+ let whenTranslateRetrying = async ((id, item), ~maxRetries, mock) => {
130
+ let result = ref(await mock(id, item))
131
+ let retries = ref(0)
132
+ let failed = () =>
133
+ switch result.contents {
134
+ | Error(_) => true
135
+ | Ok(_) => false
136
+ }
137
+ while failed() && retries.contents < maxRetries {
138
+ retries := retries.contents + 1
139
+ result := await mock(id, item)
140
+ }
141
+ {id, item, result: result.contents, retries: retries.contents}
142
+ }
143
+
144
+ let commandPairJson = (id, cmd) => {
145
+ let d = Dict.make()
146
+ d->Dict.set("id", JSON.Encode.string(id))
147
+ d->Dict.set("command", encInbound(cmd))
148
+ JSON.Encode.object(d)
149
+ }
150
+
151
+ let thenCommand = async (pending, expectedId, expectedCmd) => {
152
+ let {result, _} = await pending
153
+ switch result {
154
+ | Ok(Some((id, cmd))) if id == expectedId && cmd == expectedCmd => Outcome.pass
155
+ | Ok(Some((id, cmd))) =>
156
+ Outcome.fail(
157
+ EventsMismatch({
158
+ expected: [commandPairJson(expectedId, expectedCmd)],
159
+ actual: [commandPairJson(id, cmd)],
160
+ }),
161
+ )
162
+ | Ok(None) =>
163
+ Outcome.fail(
164
+ EventsMismatch({
165
+ expected: [commandPairJson(expectedId, expectedCmd)],
166
+ actual: [],
167
+ }),
168
+ )
169
+ | Error(msg) =>
170
+ Outcome.fail(
171
+ TranslateError({expected: "(command)", actual: Some(msg)}),
172
+ )
173
+ }
174
+ }
175
+
176
+ let thenNoCommand = async pending => {
177
+ let {result, _} = await pending
178
+ switch result {
179
+ | Ok(None) => Outcome.pass
180
+ | Ok(Some((id, cmd))) =>
181
+ Outcome.fail(NoEventExpected({actual: [commandPairJson(id, cmd)]}))
182
+ | Error(msg) =>
183
+ Outcome.fail(
184
+ TranslateError({expected: "(no command)", actual: Some(msg)}),
185
+ )
186
+ }
187
+ }
188
+
189
+ // `thenRetryRecorded(n)` — asserts the harness spent exactly `n` retries.
190
+ // Pair with `whenTranslateRetrying(~maxRetries)`, which re-invokes the mock on
191
+ // each failure and tracks the count; a single `whenTranslateMocked` records 0.
192
+ let thenRetryRecorded = async (pending, expectedRetries) => {
193
+ let {retries, _} = await pending
194
+ if retries == expectedRetries {
195
+ Outcome.pass
196
+ } else {
197
+ Outcome.fail(
198
+ StateMismatch({
199
+ key: "retries",
200
+ expected: Some(JSON.Encode.int(expectedRetries)),
201
+ actual: Some(JSON.Encode.int(retries)),
202
+ }),
203
+ )
204
+ }
205
+ }
206
+
207
+ let statusToString = (s: todoStatus) =>
208
+ switch s {
209
+ | #Completed => "#Completed"
210
+ | #Pending => "#Pending"
211
+ }
212
+
213
+ let thenTodoStatus = async (pending, expectedId, expectedStatus) => {
214
+ let {id, result, _} = await pending
215
+ let actualStatus: todoStatus =
216
+ switch result {
217
+ | Ok(_) => #Completed
218
+ | Error(_) => #Pending
219
+ }
220
+ if id == expectedId && actualStatus == expectedStatus {
221
+ Outcome.pass
222
+ } else {
223
+ let encOne = (rid, rstatus) => {
224
+ let d = Dict.make()
225
+ d->Dict.set("id", JSON.Encode.string(rid))
226
+ d->Dict.set("status", JSON.Encode.string(statusToString(rstatus)))
227
+ JSON.Encode.object(d)
228
+ }
229
+ Outcome.fail(
230
+ StateMismatch({
231
+ key: id,
232
+ expected: Some(encOne(expectedId, expectedStatus)),
233
+ actual: Some(encOne(id, actualStatus)),
234
+ }),
235
+ )
236
+ }
237
+ }
238
+ }
@@ -0,0 +1,186 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
5
+ import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
6
+ import * as JestBind$ReventlessGwt from "./JestBind.res.mjs";
7
+ import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
8
+
9
+ function Make(Spec) {
10
+ S.enableJson();
11
+ let test = (name, timeout, body) => JestBind$ReventlessGwt.testPromise(Spec.name, name, timeout, body);
12
+ let testSync = (name, body) => JestBind$ReventlessGwt.test(Spec.name, name, body);
13
+ let encItems = arr => arr.map(param => [
14
+ param[0],
15
+ Message$ReventlessCore.encode(param[1], Spec.outboundItemSchema)
16
+ ]);
17
+ let givenEvent = e => e;
18
+ let whenCollect = e => Spec.collect(e);
19
+ let thenTodos = (actual, expected) => {
20
+ if (Primitive_object.equal(actual, expected)) {
21
+ return Outcome$ReventlessGwt.pass;
22
+ } else {
23
+ return Outcome$ReventlessGwt.fail({
24
+ TAG: "TodoMismatch",
25
+ expected: encItems(expected),
26
+ actual: encItems(actual)
27
+ });
28
+ }
29
+ };
30
+ let givenTodo = (id, item) => [
31
+ id,
32
+ item
33
+ ];
34
+ let whenTranslateMocked = async (param, mock) => {
35
+ let item = param[1];
36
+ let id = param[0];
37
+ let result = await mock(id, item);
38
+ return {
39
+ id: id,
40
+ item: item,
41
+ result: result,
42
+ retries: 0
43
+ };
44
+ };
45
+ let whenTranslateRetrying = async (param, maxRetries, mock) => {
46
+ let item = param[1];
47
+ let id = param[0];
48
+ let result = {
49
+ contents: await mock(id, item)
50
+ };
51
+ let retries = 0;
52
+ let failed = () => {
53
+ let match = result.contents;
54
+ return match.TAG !== "Ok";
55
+ };
56
+ while (failed() && retries < maxRetries) {
57
+ retries = retries + 1 | 0;
58
+ result.contents = await mock(id, item);
59
+ };
60
+ return {
61
+ id: id,
62
+ item: item,
63
+ result: result.contents,
64
+ retries: retries
65
+ };
66
+ };
67
+ let commandPairJson = (id, cmd) => {
68
+ let d = {};
69
+ d["id"] = id;
70
+ d["command"] = Message$ReventlessCore.encode(cmd, Spec.inboundCommandSchema);
71
+ return d;
72
+ };
73
+ let thenCommand = async (pending, expectedId, expectedCmd) => {
74
+ let match = await pending;
75
+ let result = match.result;
76
+ if (result.TAG !== "Ok") {
77
+ return Outcome$ReventlessGwt.fail({
78
+ TAG: "TranslateError",
79
+ expected: "(command)",
80
+ actual: result._0
81
+ });
82
+ }
83
+ let match$1 = result._0;
84
+ if (match$1 === undefined) {
85
+ return Outcome$ReventlessGwt.fail({
86
+ TAG: "EventsMismatch",
87
+ expected: [commandPairJson(expectedId, expectedCmd)],
88
+ actual: []
89
+ });
90
+ }
91
+ let cmd = match$1[1];
92
+ let id = match$1[0];
93
+ if (id === expectedId && Primitive_object.equal(cmd, expectedCmd)) {
94
+ return Outcome$ReventlessGwt.pass;
95
+ } else {
96
+ return Outcome$ReventlessGwt.fail({
97
+ TAG: "EventsMismatch",
98
+ expected: [commandPairJson(expectedId, expectedCmd)],
99
+ actual: [commandPairJson(id, cmd)]
100
+ });
101
+ }
102
+ };
103
+ let thenNoCommand = async pending => {
104
+ let match = await pending;
105
+ let result = match.result;
106
+ if (result.TAG !== "Ok") {
107
+ return Outcome$ReventlessGwt.fail({
108
+ TAG: "TranslateError",
109
+ expected: "(no command)",
110
+ actual: result._0
111
+ });
112
+ }
113
+ let match$1 = result._0;
114
+ if (match$1 !== undefined) {
115
+ return Outcome$ReventlessGwt.fail({
116
+ TAG: "NoEventExpected",
117
+ actual: [commandPairJson(match$1[0], match$1[1])]
118
+ });
119
+ } else {
120
+ return Outcome$ReventlessGwt.pass;
121
+ }
122
+ };
123
+ let thenRetryRecorded = async (pending, expectedRetries) => {
124
+ let match = await pending;
125
+ let retries = match.retries;
126
+ if (retries === expectedRetries) {
127
+ return Outcome$ReventlessGwt.pass;
128
+ } else {
129
+ return Outcome$ReventlessGwt.fail({
130
+ TAG: "StateMismatch",
131
+ key: "retries",
132
+ expected: expectedRetries,
133
+ actual: retries
134
+ });
135
+ }
136
+ };
137
+ let statusToString = s => {
138
+ if (s === "Pending") {
139
+ return "#Pending";
140
+ } else {
141
+ return "#Completed";
142
+ }
143
+ };
144
+ let thenTodoStatus = async (pending, expectedId, expectedStatus) => {
145
+ let match = await pending;
146
+ let id = match.id;
147
+ let actualStatus;
148
+ actualStatus = match.result.TAG === "Ok" ? "Completed" : "Pending";
149
+ if (id === expectedId && actualStatus === expectedStatus) {
150
+ return Outcome$ReventlessGwt.pass;
151
+ }
152
+ let encOne = (rid, rstatus) => {
153
+ let d = {};
154
+ d["id"] = rid;
155
+ d["status"] = statusToString(rstatus);
156
+ return d;
157
+ };
158
+ return Outcome$ReventlessGwt.fail({
159
+ TAG: "StateMismatch",
160
+ key: id,
161
+ expected: encOne(expectedId, expectedStatus),
162
+ actual: encOne(id, actualStatus)
163
+ });
164
+ };
165
+ return {
166
+ Spec: Spec,
167
+ describe: JestBind$ReventlessGwt.describe,
168
+ test: test,
169
+ testSync: testSync,
170
+ givenEvent: givenEvent,
171
+ whenCollect: whenCollect,
172
+ thenTodos: thenTodos,
173
+ givenTodo: givenTodo,
174
+ whenTranslateMocked: whenTranslateMocked,
175
+ whenTranslateRetrying: whenTranslateRetrying,
176
+ thenCommand: thenCommand,
177
+ thenNoCommand: thenNoCommand,
178
+ thenRetryRecorded: thenRetryRecorded,
179
+ thenTodoStatus: thenTodoStatus
180
+ };
181
+ }
182
+
183
+ export {
184
+ Make,
185
+ }
186
+ /* S Not a pure module */
@@ -0,0 +1,97 @@
1
+ // The `Outcome` algebra — Stage 2 of the reventless-gwt plan.
2
+ //
3
+ // Every `then*` combinator in every GWT DSL returns `Outcome.outcome`
4
+ // (`result<unit, mismatch>`). The runner, the human formatter, the AI loop,
5
+ // and the IDE all consume the same `Outcome` value, just rendered differently.
6
+ //
7
+ // See `docs/analysis/given-when-then-specifications.md` §3.2 for the canonical
8
+ // shape and §3.3 for the JSON rendering. Stage 7 adds sury-aware rendering and
9
+ // structural `fieldDiff`. Stage 4 added `AppendConditionMismatch`.
10
+
11
+ type mismatch =
12
+ | EventsMismatch({expected: array<JSON.t>, actual: array<JSON.t>})
13
+ | ErrorMismatch({
14
+ expected: JSON.t,
15
+ actual: option<JSON.t>,
16
+ actualEvents: array<JSON.t>,
17
+ })
18
+ | StateMismatch({key: string, expected: option<JSON.t>, actual: option<JSON.t>})
19
+ | NoEventExpected({actual: array<JSON.t>})
20
+ | TodoMismatch({expected: array<(string, JSON.t)>, actual: array<(string, JSON.t)>})
21
+ | AppendConditionMismatch({expected: JSON.t, actual: JSON.t})
22
+ | TranslateError({expected: string, actual: option<string>})
23
+ | QueryRowsMismatch({expected: array<JSON.t>, actual: array<JSON.t>})
24
+ // The set of published actions (events/commands) emitted by an ExtensionPoint
25
+ // mapping or an Extension delegate diverged from the expectation. Supports
26
+ // one-to-many fan-out (one inbound message → N published actions). Used by
27
+ // `Delegate_GWT` and the cross-plugin `Flow_GWT` boundary steps.
28
+ | PublishedActionsMismatch({expected: array<JSON.t>, actual: array<JSON.t>})
29
+ | Throw({error: string, stack: string})
30
+
31
+ type outcome = result<unit, mismatch>
32
+
33
+ let pass: outcome = Ok()
34
+ let fail = (m): outcome => Error(m)
35
+
36
+ let kindName = (m: mismatch) =>
37
+ switch m {
38
+ | EventsMismatch(_) => "EventsMismatch"
39
+ | ErrorMismatch(_) => "ErrorMismatch"
40
+ | StateMismatch(_) => "StateMismatch"
41
+ | NoEventExpected(_) => "NoEventExpected"
42
+ | TodoMismatch(_) => "TodoMismatch"
43
+ | AppendConditionMismatch(_) => "AppendConditionMismatch"
44
+ | TranslateError(_) => "TranslateError"
45
+ | QueryRowsMismatch(_) => "QueryRowsMismatch"
46
+ | PublishedActionsMismatch(_) => "PublishedActionsMismatch"
47
+ | Throw(_) => "Throw"
48
+ }
49
+
50
+ let stringifyJson = (j: JSON.t) => JSON.stringify(j, ~space=2)
51
+ let stringifyOptJson = (j: option<JSON.t>) =>
52
+ switch j {
53
+ | Some(v) => stringifyJson(v)
54
+ | None => "(none)"
55
+ }
56
+ let stringifyJsonArray = (arr: array<JSON.t>) =>
57
+ "[" ++ arr->Array.map(stringifyJson)->Array.join(", ") ++ "]"
58
+
59
+ let format = (m: mismatch) =>
60
+ switch m {
61
+ | EventsMismatch({expected, actual}) =>
62
+ `EventsMismatch:\n expected: ${stringifyJsonArray(expected)}\n actual: ${stringifyJsonArray(
63
+ actual,
64
+ )}`
65
+ | ErrorMismatch({expected, actual, actualEvents}) =>
66
+ `ErrorMismatch:\n expected error: ${stringifyJson(expected)}\n actual error: ${stringifyOptJson(
67
+ actual,
68
+ )}\n actual events: ${stringifyJsonArray(actualEvents)}`
69
+ | StateMismatch({key, expected, actual}) =>
70
+ `StateMismatch (key: ${key}):\n expected: ${stringifyOptJson(
71
+ expected,
72
+ )}\n actual: ${stringifyOptJson(actual)}`
73
+ | NoEventExpected({actual}) =>
74
+ `NoEventExpected: expected no events, got:\n ${stringifyJsonArray(actual)}`
75
+ | TodoMismatch({expected, actual}) => {
76
+ let fmt = arr =>
77
+ arr
78
+ ->Array.map(((id, j)) => `(${id}, ${stringifyJson(j)})`)
79
+ ->Array.join(", ")
80
+ `TodoMismatch:\n expected: [${fmt(expected)}]\n actual: [${fmt(actual)}]`
81
+ }
82
+ | AppendConditionMismatch({expected, actual}) =>
83
+ `AppendConditionMismatch:\n expected: ${stringifyJson(expected)}\n actual: ${stringifyJson(
84
+ actual,
85
+ )}`
86
+ | TranslateError({expected, actual}) =>
87
+ `TranslateError:\n expected: ${expected}\n actual: ${actual->Option.getOr("(none)")}`
88
+ | QueryRowsMismatch({expected, actual}) =>
89
+ `QueryRowsMismatch:\n expected: ${stringifyJsonArray(expected)}\n actual: ${stringifyJsonArray(
90
+ actual,
91
+ )}`
92
+ | PublishedActionsMismatch({expected, actual}) =>
93
+ `PublishedActionsMismatch:\n expected: ${stringifyJsonArray(expected)}\n actual: ${stringifyJsonArray(
94
+ actual,
95
+ )}`
96
+ | Throw({error, stack}) => `Throw: ${error}\n${stack}`
97
+ }
@@ -0,0 +1,93 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+
5
+ function fail(m) {
6
+ return {
7
+ TAG: "Error",
8
+ _0: m
9
+ };
10
+ }
11
+
12
+ function kindName(m) {
13
+ switch (m.TAG) {
14
+ case "EventsMismatch" :
15
+ return "EventsMismatch";
16
+ case "ErrorMismatch" :
17
+ return "ErrorMismatch";
18
+ case "StateMismatch" :
19
+ return "StateMismatch";
20
+ case "NoEventExpected" :
21
+ return "NoEventExpected";
22
+ case "TodoMismatch" :
23
+ return "TodoMismatch";
24
+ case "AppendConditionMismatch" :
25
+ return "AppendConditionMismatch";
26
+ case "TranslateError" :
27
+ return "TranslateError";
28
+ case "QueryRowsMismatch" :
29
+ return "QueryRowsMismatch";
30
+ case "PublishedActionsMismatch" :
31
+ return "PublishedActionsMismatch";
32
+ case "Throw" :
33
+ return "Throw";
34
+ }
35
+ }
36
+
37
+ function stringifyJson(j) {
38
+ return JSON.stringify(j, undefined, 2);
39
+ }
40
+
41
+ function stringifyOptJson(j) {
42
+ if (j !== undefined) {
43
+ return JSON.stringify(j, undefined, 2);
44
+ } else {
45
+ return "(none)";
46
+ }
47
+ }
48
+
49
+ function stringifyJsonArray(arr) {
50
+ return "[" + arr.map(stringifyJson).join(", ") + "]";
51
+ }
52
+
53
+ function format(m) {
54
+ switch (m.TAG) {
55
+ case "EventsMismatch" :
56
+ return `EventsMismatch:\n expected: ` + stringifyJsonArray(m.expected) + `\n actual: ` + stringifyJsonArray(m.actual);
57
+ case "ErrorMismatch" :
58
+ return `ErrorMismatch:\n expected error: ` + JSON.stringify(m.expected, undefined, 2) + `\n actual error: ` + stringifyOptJson(m.actual) + `\n actual events: ` + stringifyJsonArray(m.actualEvents);
59
+ case "StateMismatch" :
60
+ return `StateMismatch (key: ` + m.key + `):\n expected: ` + stringifyOptJson(m.expected) + `\n actual: ` + stringifyOptJson(m.actual);
61
+ case "NoEventExpected" :
62
+ return `NoEventExpected: expected no events, got:\n ` + stringifyJsonArray(m.actual);
63
+ case "TodoMismatch" :
64
+ let fmt = arr => arr.map(param => `(` + param[0] + `, ` + JSON.stringify(param[1], undefined, 2) + `)`).join(", ");
65
+ return `TodoMismatch:\n expected: [` + fmt(m.expected) + `]\n actual: [` + fmt(m.actual) + `]`;
66
+ case "AppendConditionMismatch" :
67
+ return `AppendConditionMismatch:\n expected: ` + JSON.stringify(m.expected, undefined, 2) + `\n actual: ` + JSON.stringify(m.actual, undefined, 2);
68
+ case "TranslateError" :
69
+ return `TranslateError:\n expected: ` + m.expected + `\n actual: ` + Stdlib_Option.getOr(m.actual, "(none)");
70
+ case "QueryRowsMismatch" :
71
+ return `QueryRowsMismatch:\n expected: ` + stringifyJsonArray(m.expected) + `\n actual: ` + stringifyJsonArray(m.actual);
72
+ case "PublishedActionsMismatch" :
73
+ return `PublishedActionsMismatch:\n expected: ` + stringifyJsonArray(m.expected) + `\n actual: ` + stringifyJsonArray(m.actual);
74
+ case "Throw" :
75
+ return `Throw: ` + m.error + `\n` + m.stack;
76
+ }
77
+ }
78
+
79
+ let pass = {
80
+ TAG: "Ok",
81
+ _0: undefined
82
+ };
83
+
84
+ export {
85
+ pass,
86
+ fail,
87
+ kindName,
88
+ stringifyJson,
89
+ stringifyOptJson,
90
+ stringifyJsonArray,
91
+ format,
92
+ }
93
+ /* No side effect */
@@ -0,0 +1,80 @@
1
+ // Derives the set of workspace packages that own GWT test files, so a watch
2
+ // session knows which packages to keep compiling (`pnpm run start` →
3
+ // `rescript build -w`). Given the absolute test-file paths from `Discovery`,
4
+ // each is mapped to its nearest ancestor `package.json` that declares a
5
+ // `start` script; results are deduplicated by directory.
6
+ //
7
+ // This is the input both the `packages` NDJSON event (FormatterVsCode) and the
8
+ // watch-mode build manager (ProcessManager) consume — computed fresh per run so
9
+ // it never drifts when a plugin is added or removed.
10
+
11
+ type pkg = {
12
+ // Package name from package.json (may be "" if absent).
13
+ name: string,
14
+ // Absolute directory containing the owning package.json.
15
+ dir: string,
16
+ // The `start` script's command (the ReScript watch command), informational.
17
+ build: string,
18
+ }
19
+
20
+ @module("node:fs") external _existsSync: string => bool = "existsSync"
21
+ @module("node:fs") external _readFileSync: (string, string) => string = "readFileSync"
22
+ @module("node:path") external dirname: string => string = "dirname"
23
+ @module("node:path") external join: (string, string) => string = "join"
24
+
25
+ // Reads name + `start` command from a package.json, if it declares one.
26
+ let readPackage = (pkgJsonPath: string): option<(string, string)> =>
27
+ try {
28
+ let json = JSON.parseOrThrow(_readFileSync(pkgJsonPath, "utf8"))
29
+ switch json {
30
+ | Object(obj) =>
31
+ let name = switch obj->Dict.get("name") {
32
+ | Some(String(n)) => n
33
+ | _ => ""
34
+ }
35
+ switch obj->Dict.get("scripts") {
36
+ | Some(Object(scripts)) =>
37
+ switch scripts->Dict.get("start") {
38
+ | Some(String(cmd)) => Some((name, cmd))
39
+ | _ => None
40
+ }
41
+ | _ => None
42
+ }
43
+ | _ => None
44
+ }
45
+ } catch {
46
+ | _ => None
47
+ }
48
+
49
+ // Walks up from `dir` to the nearest package.json declaring a `start` script.
50
+ let rec findOwning = (dir: string): option<pkg> => {
51
+ let pkgJson = join(dir, "package.json")
52
+ let here = _existsSync(pkgJson) ? readPackage(pkgJson) : None
53
+ switch here {
54
+ | Some((name, build)) => Some({name, dir, build})
55
+ | None => {
56
+ let parent = dirname(dir)
57
+ parent == dir ? None : findOwning(parent)
58
+ }
59
+ }
60
+ }
61
+
62
+ // Maps test files → deduplicated owning packages (declaration order preserved).
63
+ let scan = (testFiles: array<string>): array<pkg> => {
64
+ let seen = Dict.make()
65
+ let out = []
66
+ testFiles->Array.forEach(file =>
67
+ switch findOwning(dirname(file)) {
68
+ | Some(pkg) =>
69
+ switch seen->Dict.get(pkg.dir) {
70
+ | Some(_) => ()
71
+ | None => {
72
+ seen->Dict.set(pkg.dir, true)
73
+ out->Array.push(pkg)
74
+ }
75
+ }
76
+ | None => ()
77
+ }
78
+ )
79
+ out
80
+ }