@reventlessdev/reventless-gwt 1.0.0-alpha.74

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 (172) hide show
  1. package/CHANGELOG.md +698 -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 +603 -0
  10. package/src/Behavior_GWT.res.mjs +426 -0
  11. package/src/Bind.res +29 -0
  12. package/src/Bind.res.mjs +22 -0
  13. package/src/BuildClassifier.res +137 -0
  14. package/src/BuildClassifier.res.mjs +136 -0
  15. package/src/Cancellation.res +31 -0
  16. package/src/Cancellation.res.mjs +45 -0
  17. package/src/ChildProcess.res +84 -0
  18. package/src/ChildProcess.res.mjs +92 -0
  19. package/src/Cli.res +671 -0
  20. package/src/Cli.res.mjs +754 -0
  21. package/src/Collector.res +201 -0
  22. package/src/Collector.res.mjs +215 -0
  23. package/src/ComponentMeta.res +104 -0
  24. package/src/ComponentMeta.res.mjs +120 -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 +103 -0
  33. package/src/DomainDeadCode.res +93 -0
  34. package/src/DomainDeadCode.res.mjs +69 -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 +36 -0
  40. package/src/Filter.res.mjs +40 -0
  41. package/src/Flow_GWT.res +664 -0
  42. package/src/Flow_GWT.res.mjs +604 -0
  43. package/src/FormatterHuman.res +138 -0
  44. package/src/FormatterHuman.res.mjs +122 -0
  45. package/src/FormatterJson.res +257 -0
  46. package/src/FormatterJson.res.mjs +320 -0
  47. package/src/FormatterJunit.res +93 -0
  48. package/src/FormatterJunit.res.mjs +90 -0
  49. package/src/FormatterTap.res +139 -0
  50. package/src/FormatterTap.res.mjs +149 -0
  51. package/src/FormatterVsCode.res +362 -0
  52. package/src/FormatterVsCode.res.mjs +564 -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 +135 -0
  62. package/src/LocalHost.res.mjs +127 -0
  63. package/src/Mapping_GWT.res +474 -0
  64. package/src/Mapping_GWT.res.mjs +322 -0
  65. package/src/MultiSourceProjection_GWT.res +316 -0
  66. package/src/MultiSourceProjection_GWT.res.mjs +354 -0
  67. package/src/OutboundTranslation_GWT.res +238 -0
  68. package/src/OutboundTranslation_GWT.res.mjs +186 -0
  69. package/src/Outcome.res +161 -0
  70. package/src/Outcome.res.mjs +140 -0
  71. package/src/PackageScan.res +80 -0
  72. package/src/PackageScan.res.mjs +81 -0
  73. package/src/PlatformRunner.res +160 -0
  74. package/src/PlatformRunner.res.mjs +155 -0
  75. package/src/PlatformScan.res +138 -0
  76. package/src/PlatformScan.res.mjs +131 -0
  77. package/src/ProcessManager.res +91 -0
  78. package/src/ProcessManager.res.mjs +80 -0
  79. package/src/Projection_GWT.res +312 -0
  80. package/src/Projection_GWT.res.mjs +332 -0
  81. package/src/Query_GWT.res +379 -0
  82. package/src/Query_GWT.res.mjs +367 -0
  83. package/src/RenderRescript.res +125 -0
  84. package/src/RenderRescript.res.mjs +141 -0
  85. package/src/RunnerTypes.res +68 -0
  86. package/src/RunnerTypes.res.mjs +60 -0
  87. package/src/SideEffect_GWT.res +157 -0
  88. package/src/SideEffect_GWT.res.mjs +113 -0
  89. package/src/StateChange/ExternalAddCategorySlice.res +25 -0
  90. package/src/StateChange/ExternalAddCategorySlice.res.mjs +34 -0
  91. package/src/StateChange/ExternalAddCategorySlice_Behavior.res +19 -0
  92. package/src/StateChange/ExternalAddCategorySlice_Behavior.res.mjs +46 -0
  93. package/src/StateChange/WithFixtures.res +22 -0
  94. package/src/StateChange/WithFixtures.res.mjs +31 -0
  95. package/src/StateChange/WithFixtures_Behavior.res +17 -0
  96. package/src/StateChange/WithFixtures_Behavior.res.mjs +37 -0
  97. package/src/StateChange/WithManualOpen.res +23 -0
  98. package/src/StateChange/WithManualOpen.res.mjs +31 -0
  99. package/src/StateChange/WithManualOpen_Behavior.res +17 -0
  100. package/src/StateChange/WithManualOpen_Behavior.res.mjs +37 -0
  101. package/src/StubRuntime.res +53 -0
  102. package/src/StubRuntime.res.mjs +54 -0
  103. package/src/TestFixtures.res +16 -0
  104. package/src/TestFixtures.res.mjs +34 -0
  105. package/src/Watch.res +92 -0
  106. package/src/Watch.res.mjs +83 -0
  107. package/src/WatcherProbe.res +48 -0
  108. package/src/WatcherProbe.res.mjs +46 -0
  109. package/tests/AutomationGwtTest.res +71 -0
  110. package/tests/AutomationGwtTest.res.mjs +189 -0
  111. package/tests/BuildClassifierTest.res +94 -0
  112. package/tests/BuildClassifierTest.res.mjs +132 -0
  113. package/tests/ComponentMetaTest.res +52 -0
  114. package/tests/ComponentMetaTest.res.mjs +57 -0
  115. package/tests/DelegateGwtTest.res +487 -0
  116. package/tests/DelegateGwtTest.res.mjs +891 -0
  117. package/tests/DiscoveryTest.res +42 -0
  118. package/tests/DiscoveryTest.res.mjs +43 -0
  119. package/tests/DomainDeadCodeTest.res +116 -0
  120. package/tests/DomainDeadCodeTest.res.mjs +155 -0
  121. package/tests/DomainGraphTest.res +365 -0
  122. package/tests/DomainGraphTest.res.mjs +413 -0
  123. package/tests/FlowAggregateGwtTest.res +169 -0
  124. package/tests/FlowAggregateGwtTest.res.mjs +269 -0
  125. package/tests/FlowCrossPluginGwtTest.res +340 -0
  126. package/tests/FlowCrossPluginGwtTest.res.mjs +798 -0
  127. package/tests/FlowGwtTest.res +222 -0
  128. package/tests/FlowGwtTest.res.mjs +432 -0
  129. package/tests/FormatterVsCodeMessageTest.res +41 -0
  130. package/tests/FormatterVsCodeMessageTest.res.mjs +42 -0
  131. package/tests/InboundTranslationGwtTest.res +34 -0
  132. package/tests/InboundTranslationGwtTest.res.mjs +98 -0
  133. package/tests/LocalHostIntegration.res +70 -0
  134. package/tests/LocalHostIntegration.res.mjs +64 -0
  135. package/tests/LocalHostTest.res +71 -0
  136. package/tests/LocalHostTest.res.mjs +63 -0
  137. package/tests/MappingGwtTest.res +348 -0
  138. package/tests/MappingGwtTest.res.mjs +592 -0
  139. package/tests/OutboundTranslationGwtTest.res +59 -0
  140. package/tests/OutboundTranslationGwtTest.res.mjs +138 -0
  141. package/tests/PlatformRunnerTest.res +66 -0
  142. package/tests/PlatformRunnerTest.res.mjs +85 -0
  143. package/tests/PlatformScanTest.res +61 -0
  144. package/tests/PlatformScanTest.res.mjs +56 -0
  145. package/tests/QueryGwtTest.res +161 -0
  146. package/tests/QueryGwtTest.res.mjs +314 -0
  147. package/tests/QueryResolverGwtTest.res +71 -0
  148. package/tests/QueryResolverGwtTest.res.mjs +136 -0
  149. package/tests/RunnerUnitTest.res +219 -0
  150. package/tests/RunnerUnitTest.res.mjs +244 -0
  151. package/tests/SideEffect/FakeOrderNotification.res +23 -0
  152. package/tests/SideEffect/FakeOrderNotification.res.mjs +36 -0
  153. package/tests/SideEffect/FakeOrderNotificationGwtTest.res +35 -0
  154. package/tests/SideEffect/FakeOrderNotificationGwtTest.res.mjs +73 -0
  155. package/tests/SideEffect/MockEmail.res +18 -0
  156. package/tests/SideEffect/MockEmail.res.mjs +47 -0
  157. package/tests/StateChange/ExternalAddCategorySlice_GWT.res +27 -0
  158. package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +70 -0
  159. package/tests/StateChange/WithFixtures_Fixtures.res +9 -0
  160. package/tests/StateChange/WithFixtures_Fixtures.res.mjs +20 -0
  161. package/tests/StateChange/WithFixtures_GWT.res +23 -0
  162. package/tests/StateChange/WithFixtures_GWT.res.mjs +56 -0
  163. package/tests/StateChange/WithManualOpen_Fixtures.res +7 -0
  164. package/tests/StateChange/WithManualOpen_Fixtures.res.mjs +20 -0
  165. package/tests/StateChange/WithManualOpen_GWT.res +17 -0
  166. package/tests/StateChange/WithManualOpen_GWT.res.mjs +56 -0
  167. package/tests/StateChangeSliceGwtTest.res +160 -0
  168. package/tests/StateChangeSliceGwtTest.res.mjs +280 -0
  169. package/tests/StateViewSliceGwtTest.res +64 -0
  170. package/tests/StateViewSliceGwtTest.res.mjs +161 -0
  171. package/tests/WatchDebounceTest.res +78 -0
  172. package/tests/WatchDebounceTest.res.mjs +97 -0
@@ -0,0 +1,280 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
5
+ import * as Outcome$ReventlessGwt from "../src/Outcome.res.mjs";
6
+ import * as Behavior_GWT$ReventlessGwt from "../src/Behavior_GWT.res.mjs";
7
+
8
+ let consumedEventSchema = S.union([
9
+ S.literal("CategoryAdded"),
10
+ S.literal("CategoryArchived")
11
+ ]);
12
+
13
+ let commandSchema = S.schema(s => ({
14
+ TAG: "AddCategory",
15
+ categoryId: s.m(DcbTag$Reventless.string),
16
+ name: s.m(S.string)
17
+ }));
18
+
19
+ let errorSchema = S.literal("CategoryAlreadyExists");
20
+
21
+ let eventSchema = S.schema(s => ({
22
+ TAG: "CategoryAdded",
23
+ categoryId: s.m(DcbTag$Reventless.string),
24
+ name: s.m(S.string)
25
+ }));
26
+
27
+ function commandAuthorization(param) {
28
+ return "AllowAuthenticated";
29
+ }
30
+
31
+ let AddCategorySlice = {
32
+ name: "AddCategory",
33
+ consumedEventSchema: consumedEventSchema,
34
+ commandSchema: commandSchema,
35
+ errorSchema: errorSchema,
36
+ eventSchema: eventSchema,
37
+ commandAuthorization: commandAuthorization,
38
+ readConsistency: "EscalateOnRetry"
39
+ };
40
+
41
+ let initialState = {
42
+ exists: false,
43
+ archived: false
44
+ };
45
+
46
+ function evolve(state, event) {
47
+ if (event === "CategoryAdded") {
48
+ return {
49
+ exists: true,
50
+ archived: false
51
+ };
52
+ } else {
53
+ return {
54
+ exists: state.exists,
55
+ archived: true
56
+ };
57
+ }
58
+ }
59
+
60
+ function decide(state, command) {
61
+ if (state.exists) {
62
+ return {
63
+ TAG: "Error",
64
+ _0: "CategoryAlreadyExists"
65
+ };
66
+ } else {
67
+ return {
68
+ TAG: "Ok",
69
+ _0: [{
70
+ TAG: "CategoryAdded",
71
+ categoryId: command.categoryId,
72
+ name: command.name
73
+ }]
74
+ };
75
+ }
76
+ }
77
+
78
+ let AddCategorySliceBehavior = {
79
+ Spec: undefined,
80
+ initialState: initialState,
81
+ evolve: evolve,
82
+ decide: decide
83
+ };
84
+
85
+ let include = Behavior_GWT$ReventlessGwt.Make(AddCategorySlice)({
86
+ initialState: initialState,
87
+ evolve: evolve,
88
+ decide: decide
89
+ });
90
+
91
+ let describe = include.describe;
92
+
93
+ let test = include.test;
94
+
95
+ let givenEvents = include.givenEvents;
96
+
97
+ let whenCmd = include.whenCmd;
98
+
99
+ let thenEvent = include.thenEvent;
100
+
101
+ let thenError = include.thenError;
102
+
103
+ let thenAppendsConditionedOn = include.thenAppendsConditionedOn;
104
+
105
+ let thenAppendsConditionedOnExactly = include.thenAppendsConditionedOnExactly;
106
+
107
+ describe("AddCategory StateChangeSlice", () => {
108
+ test("on empty event log produces CategoryAdded", () => thenEvent(whenCmd(givenEvents([]), {
109
+ TAG: "AddCategory",
110
+ categoryId: "c1",
111
+ name: "Electronics"
112
+ }), {
113
+ TAG: "CategoryAdded",
114
+ categoryId: "c1",
115
+ name: "Electronics"
116
+ }));
117
+ test("on existing category returns CategoryAlreadyExists", () => thenError(whenCmd(givenEvents(["CategoryAdded"]), {
118
+ TAG: "AddCategory",
119
+ categoryId: "c1",
120
+ name: "X"
121
+ }), "CategoryAlreadyExists"));
122
+ test("append condition is single-entity query over consumed event types", () => thenAppendsConditionedOn(whenCmd(givenEvents([]), {
123
+ TAG: "AddCategory",
124
+ categoryId: "c1",
125
+ name: "Electronics"
126
+ }), [{
127
+ eventTypes: [
128
+ "CategoryAdded",
129
+ "CategoryArchived"
130
+ ],
131
+ tags: [{
132
+ key: "categoryId",
133
+ value: "c1"
134
+ }]
135
+ }]));
136
+ test("exact condition matches no prior position (new entity)", () => thenAppendsConditionedOnExactly(whenCmd(givenEvents([]), {
137
+ TAG: "AddCategory",
138
+ categoryId: "c2",
139
+ name: "Books"
140
+ }), {
141
+ query: [{
142
+ eventTypes: [
143
+ "CategoryAdded",
144
+ "CategoryArchived"
145
+ ],
146
+ tags: [{
147
+ key: "categoryId",
148
+ value: "c2"
149
+ }]
150
+ }]
151
+ }));
152
+ });
153
+
154
+ let consumedEventSchema$1 = S.literal("Noop");
155
+
156
+ let commandSchema$1 = S.schema(s => ({
157
+ TAG: "Do",
158
+ id: s.m(S.string)
159
+ }));
160
+
161
+ let errorSchema$1 = S.literal("Rejected");
162
+
163
+ let eventSchema$1 = S.schema(s => ({
164
+ TAG: "Done",
165
+ id: s.m(S.string)
166
+ }));
167
+
168
+ function commandAuthorization$1(param) {
169
+ return "AllowAuthenticated";
170
+ }
171
+
172
+ let MissingTagSlice = {
173
+ name: "MissingTagSlice",
174
+ consumedEventSchema: consumedEventSchema$1,
175
+ commandSchema: commandSchema$1,
176
+ errorSchema: errorSchema$1,
177
+ eventSchema: eventSchema$1,
178
+ commandAuthorization: commandAuthorization$1,
179
+ readConsistency: "EscalateOnRetry"
180
+ };
181
+
182
+ function evolve$1(state, _event) {
183
+
184
+ }
185
+
186
+ function decide$1(_state, cmd) {
187
+ return {
188
+ TAG: "Ok",
189
+ _0: [{
190
+ TAG: "Done",
191
+ id: cmd.id
192
+ }]
193
+ };
194
+ }
195
+
196
+ let MissingTagBehavior = {
197
+ Spec: undefined,
198
+ initialState: undefined,
199
+ evolve: evolve$1,
200
+ decide: decide$1
201
+ };
202
+
203
+ let MissingTagGwt = Behavior_GWT$ReventlessGwt.Make(MissingTagSlice)({
204
+ initialState: undefined,
205
+ evolve: evolve$1,
206
+ decide: decide$1
207
+ });
208
+
209
+ MissingTagGwt.describe("MissingTag slice implicit check", () => {
210
+ MissingTagGwt.test("thenEvent surfaces AppendConditionMismatch when command lacks DCB tag", () => {
211
+ let outcome = MissingTagGwt.thenEvent(MissingTagGwt.whenCmd(MissingTagGwt.givenEvents([]), {
212
+ TAG: "Do",
213
+ id: "x1"
214
+ }), {
215
+ TAG: "Done",
216
+ id: "x1"
217
+ });
218
+ if (outcome.TAG === "Ok") {
219
+ return Outcome$ReventlessGwt.fail({
220
+ TAG: "Throw",
221
+ error: "expected AppendConditionMismatch, got pass",
222
+ stack: ""
223
+ });
224
+ }
225
+ let other = outcome._0;
226
+ if (other.TAG === "AppendConditionMismatch") {
227
+ return Outcome$ReventlessGwt.pass;
228
+ } else {
229
+ return Outcome$ReventlessGwt.fail({
230
+ TAG: "Throw",
231
+ error: "expected AppendConditionMismatch, got: " + Outcome$ReventlessGwt.kindName(other),
232
+ stack: ""
233
+ });
234
+ }
235
+ });
236
+ MissingTagGwt.test("thenAppendsConditionedOnExactly bypasses implicit check (still passes with derived)", () => MissingTagGwt.thenAppendsConditionedOnExactly(MissingTagGwt.whenCmd(MissingTagGwt.givenEvents([]), {
237
+ TAG: "Do",
238
+ id: "x1"
239
+ }), {
240
+ query: [{
241
+ eventTypes: ["Noop"],
242
+ tags: []
243
+ }]
244
+ }));
245
+ });
246
+
247
+ let Spec = include.Spec;
248
+
249
+ let todo = include.todo;
250
+
251
+ let thenEvents = include.thenEvents;
252
+
253
+ let thenNoEvent = include.thenNoEvent;
254
+
255
+ let thenEventWithError = include.thenEventWithError;
256
+
257
+ let thenEventsWithError = include.thenEventsWithError;
258
+
259
+ export {
260
+ AddCategorySlice,
261
+ AddCategorySliceBehavior,
262
+ Spec,
263
+ describe,
264
+ todo,
265
+ test,
266
+ givenEvents,
267
+ whenCmd,
268
+ thenEvent,
269
+ thenEvents,
270
+ thenNoEvent,
271
+ thenEventWithError,
272
+ thenEventsWithError,
273
+ thenError,
274
+ thenAppendsConditionedOn,
275
+ thenAppendsConditionedOnExactly,
276
+ MissingTagSlice,
277
+ MissingTagBehavior,
278
+ MissingTagGwt,
279
+ }
280
+ /* consumedEventSchema Not a pure module */
@@ -0,0 +1,64 @@
1
+ // Worked example for Projection_GWT (single-source StateViewSlice flavor).
2
+ // Projects a small category event stream into a `{categoryId, name, archived}`
3
+ // read model, exercising Set / Update actions.
4
+ //
5
+ // Plan 02 Phase 6: split-form Spec/Projection; PPX inference resolves the
6
+ // "StateViewSlice" filename substring → Projection DSL → emits
7
+ // [include Projection_GWT.Make(Spec, Projection)].
8
+
9
+ @@reventless.gwt
10
+
11
+ open Reventless.Projection
12
+
13
+ module CategoriesView = {
14
+ let name = "CategoriesView"
15
+
16
+ @schema
17
+ type state = {categoryId: string, name: string, archived: bool}
18
+
19
+ @schema
20
+ type consumedEvent =
21
+ | CategoryAdded({categoryId: string, name: string})
22
+ | CategoryRenamed({categoryId: string, name: string})
23
+ | CategoryArchived({categoryId: string})
24
+
25
+ let subIdConfig = None
26
+ }
27
+
28
+ module CategoriesViewProjection = {
29
+ module Spec = CategoriesView
30
+ open CategoriesView
31
+
32
+ let project = (event: consumedEvent) =>
33
+ switch event {
34
+ | CategoryAdded({categoryId, name}) => [
35
+ Set(categoryId, {categoryId, name, archived: false}),
36
+ ]
37
+ | CategoryRenamed({categoryId, name}) => [
38
+ Update(categoryId, state => {...state, name}),
39
+ ]
40
+ | CategoryArchived({categoryId}) => [
41
+ Update(categoryId, state => {...state, archived: true}),
42
+ ]
43
+ }
44
+ }
45
+
46
+ describe("CategoriesView StateViewSlice", () => {
47
+ test("projects CategoryAdded into a new row", () =>
48
+ givenEvents([])
49
+ ->whenEvent(CategoryAdded({categoryId: "c1", name: "Electronics"}))
50
+ ->thenStateWithId(
51
+ "c1",
52
+ {categoryId: "c1", name: "Electronics", archived: false},
53
+ )
54
+ )
55
+
56
+ test("CategoryRenamed updates the name", () =>
57
+ givenEvents([CategoryAdded({categoryId: "c1", name: "Electronics"})])
58
+ ->whenEvent(CategoryRenamed({categoryId: "c1", name: "Consumer Electronics"}))
59
+ ->thenStateWithId(
60
+ "c1",
61
+ {categoryId: "c1", name: "Consumer Electronics", archived: false},
62
+ )
63
+ )
64
+ })
@@ -0,0 +1,161 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Projection_GWT$ReventlessGwt from "../src/Projection_GWT.res.mjs";
5
+
6
+ let stateSchema = S.schema(s => ({
7
+ categoryId: s.m(S.string),
8
+ name: s.m(S.string),
9
+ archived: s.m(S.bool)
10
+ }));
11
+
12
+ let consumedEventSchema = S.union([
13
+ S.schema(s => ({
14
+ TAG: "CategoryAdded",
15
+ categoryId: s.m(S.string),
16
+ name: s.m(S.string)
17
+ })),
18
+ S.schema(s => ({
19
+ TAG: "CategoryRenamed",
20
+ categoryId: s.m(S.string),
21
+ name: s.m(S.string)
22
+ })),
23
+ S.schema(s => ({
24
+ TAG: "CategoryArchived",
25
+ categoryId: s.m(S.string)
26
+ }))
27
+ ]);
28
+
29
+ let CategoriesView = {
30
+ name: "CategoriesView",
31
+ stateSchema: stateSchema,
32
+ consumedEventSchema: consumedEventSchema,
33
+ subIdConfig: undefined,
34
+ authorization: "AllowAuthenticated",
35
+ visibility: "Public"
36
+ };
37
+
38
+ function project(event) {
39
+ switch (event.TAG) {
40
+ case "CategoryAdded" :
41
+ let categoryId = event.categoryId;
42
+ return [{
43
+ TAG: "Set",
44
+ _0: categoryId,
45
+ _1: {
46
+ categoryId: categoryId,
47
+ name: event.name,
48
+ archived: false
49
+ }
50
+ }];
51
+ case "CategoryRenamed" :
52
+ let name = event.name;
53
+ return [{
54
+ TAG: "Update",
55
+ _0: event.categoryId,
56
+ _1: state => ({
57
+ categoryId: state.categoryId,
58
+ name: name,
59
+ archived: state.archived
60
+ })
61
+ }];
62
+ case "CategoryArchived" :
63
+ return [{
64
+ TAG: "Update",
65
+ _0: event.categoryId,
66
+ _1: state => ({
67
+ categoryId: state.categoryId,
68
+ name: state.name,
69
+ archived: true
70
+ })
71
+ }];
72
+ }
73
+ }
74
+
75
+ let CategoriesViewProjection = {
76
+ Spec: undefined,
77
+ project: project
78
+ };
79
+
80
+ let include = Projection_GWT$ReventlessGwt.Make(CategoriesView)({
81
+ project: project
82
+ });
83
+
84
+ let describe = include.describe;
85
+
86
+ let test = include.test;
87
+
88
+ let givenEvents = include.givenEvents;
89
+
90
+ let whenEvent = include.whenEvent;
91
+
92
+ let thenStateWithId = include.thenStateWithId;
93
+
94
+ describe("CategoriesView StateViewSlice", () => {
95
+ test("projects CategoryAdded into a new row", undefined, () => thenStateWithId(whenEvent(givenEvents([]), {
96
+ TAG: "CategoryAdded",
97
+ categoryId: "c1",
98
+ name: "Electronics"
99
+ }), "c1", {
100
+ categoryId: "c1",
101
+ name: "Electronics",
102
+ archived: false
103
+ }));
104
+ test("CategoryRenamed updates the name", undefined, () => thenStateWithId(whenEvent(givenEvents([{
105
+ TAG: "CategoryAdded",
106
+ categoryId: "c1",
107
+ name: "Electronics"
108
+ }]), {
109
+ TAG: "CategoryRenamed",
110
+ categoryId: "c1",
111
+ name: "Consumer Electronics"
112
+ }), "c1", {
113
+ categoryId: "c1",
114
+ name: "Consumer Electronics",
115
+ archived: false
116
+ }));
117
+ });
118
+
119
+ let Spec = include.Spec;
120
+
121
+ let describeWithId = include.describeWithId;
122
+
123
+ let todo = include.todo;
124
+
125
+ let whenEvents = include.whenEvents;
126
+
127
+ let thenStates = include.thenStates;
128
+
129
+ let thenStatesWithId = include.thenStatesWithId;
130
+
131
+ let thenAllStates = include.thenAllStates;
132
+
133
+ let thenState = include.thenState;
134
+
135
+ let thenNoState = include.thenNoState;
136
+
137
+ let thenThrow = include.thenThrow;
138
+
139
+ let thenFail = include.thenFail;
140
+
141
+ export {
142
+ CategoriesView,
143
+ CategoriesViewProjection,
144
+ Spec,
145
+ describe,
146
+ describeWithId,
147
+ todo,
148
+ test,
149
+ givenEvents,
150
+ whenEvent,
151
+ whenEvents,
152
+ thenStates,
153
+ thenStatesWithId,
154
+ thenAllStates,
155
+ thenState,
156
+ thenStateWithId,
157
+ thenNoState,
158
+ thenThrow,
159
+ thenFail,
160
+ }
161
+ /* stateSchema Not a pure module */
@@ -0,0 +1,78 @@
1
+ // Unit tests for Watch's event coalescing — the part Phase 12 added so a
2
+ // relocation's structural `unlink` survives a debounce window that also carries
3
+ // the trailing `add`/`change` of the same `mv`. The debounce uses real
4
+ // setTimeout(120ms), so these await past the window.
5
+
6
+ open JestGlobals
7
+
8
+ @val external setTimeout: (unit => unit, int) => unit = "setTimeout"
9
+ let delay = (ms: int): promise<unit> =>
10
+ Promise.make((resolve, _reject) => setTimeout(() => resolve(), ms))
11
+
12
+ describe("Watch.isStructuralSource", () => {
13
+ testPromise("an unlink of a .res source is structural", async () =>
14
+ expect(Watch.isStructuralSource(Unlink, "/p/src/Foo/Bar.res"))->toEqual(true)
15
+ )
16
+ testPromise("a generated .res.mjs unlink is NOT structural (ends in .mjs)", async () =>
17
+ expect(Watch.isStructuralSource(Unlink, "/p/src/Foo/Bar.res.mjs"))->toEqual(false)
18
+ )
19
+ testPromise("an add of a .res source is not structural (incremental handles it)", async () =>
20
+ expect(Watch.isStructuralSource(Add, "/p/src/Foo/Bar.res"))->toEqual(false)
21
+ )
22
+ testPromise("a change of a .res source is not structural", async () =>
23
+ expect(Watch.isStructuralSource(Change, "/p/src/Foo/Bar.res"))->toEqual(false)
24
+ )
25
+ })
26
+
27
+ describe("Watch.debounce coalescing", () => {
28
+ testPromise("a burst collapses to one call carrying the strongest event", async () => {
29
+ let calls = ref([])
30
+ let fire = Watch.debounce(40, (e, p) => calls := Array.concat(calls.contents, [(e, p)]))
31
+ // A move/ungroup: the new file is created, an edit lands, then the old file
32
+ // is unlinked — all inside one window. The unlink must win.
33
+ fire(Watch.Change, "/p/src/A.res")
34
+ fire(Watch.Add, "/p/src/new/A.res")
35
+ fire(Watch.Unlink, "/p/src/old/A.res")
36
+ await delay(120)
37
+ expect(calls.contents->Array.length)->toEqual(1)
38
+ let (e, p) = calls.contents->Array.getUnsafe(0)
39
+ expect(e)->toEqual(Watch.Unlink)
40
+ expect(p)->toEqual("/p/src/old/A.res")
41
+ })
42
+
43
+ testPromise("a later weaker event does not mask an earlier unlink", async () => {
44
+ let calls = ref([])
45
+ let fire = Watch.debounce(40, (e, _p) => calls := Array.concat(calls.contents, [e]))
46
+ fire(Watch.Unlink, "/p/src/old/A.res")
47
+ fire(Watch.Change, "/p/src/A.res")
48
+ await delay(120)
49
+ expect(calls.contents)->toEqual([Watch.Unlink])
50
+ })
51
+
52
+ testPromise("interleaved .res.mjs unlinks don't steal the representative path", async () => {
53
+ // The real directory-mv burst chokidar emits: source AND generated files
54
+ // unlink interleaved. The coalesced path must be a `.res` source, not the
55
+ // last-seen `.res.mjs` (which would misclassify as a non-structural change).
56
+ let calls = ref([])
57
+ let fire = Watch.debounce(40, (e, p) => calls := Array.concat(calls.contents, [(e, p)]))
58
+ fire(Watch.Unlink, "/p/src/old/A.res")
59
+ fire(Watch.Unlink, "/p/src/old/A.res.mjs")
60
+ fire(Watch.Unlink, "/p/src/old/A_Behavior.res")
61
+ fire(Watch.Unlink, "/p/src/old/A_Behavior.res.mjs")
62
+ fire(Watch.Add, "/p/src/new/A.res")
63
+ fire(Watch.Add, "/p/src/new/A.res.mjs")
64
+ await delay(120)
65
+ expect(calls.contents->Array.length)->toEqual(1)
66
+ let (e, p) = calls.contents->Array.getUnsafe(0)
67
+ expect(e)->toEqual(Watch.Unlink)
68
+ expect(Watch.isStructuralSource(e, p))->toEqual(true)
69
+ })
70
+
71
+ testPromise("a lone change stays a change (incremental re-run)", async () => {
72
+ let calls = ref([])
73
+ let fire = Watch.debounce(40, (e, _p) => calls := Array.concat(calls.contents, [e]))
74
+ fire(Watch.Change, "/p/src/A.res")
75
+ await delay(120)
76
+ expect(calls.contents)->toEqual([Watch.Change])
77
+ })
78
+ })
@@ -0,0 +1,97 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Watch$ReventlessGwt from "../src/Watch.res.mjs";
4
+
5
+ function delay(ms) {
6
+ return new Promise((resolve, _reject) => {
7
+ setTimeout(() => resolve(), ms);
8
+ });
9
+ }
10
+
11
+ globalThis.describe("Watch.isStructuralSource", () => {
12
+ globalThis.test("an unlink of a .res source is structural", async () => {
13
+ globalThis.expect(Watch$ReventlessGwt.isStructuralSource("Unlink", "/p/src/Foo/Bar.res")).toEqual(true);
14
+ });
15
+ globalThis.test("a generated .res.mjs unlink is NOT structural (ends in .mjs)", async () => {
16
+ globalThis.expect(Watch$ReventlessGwt.isStructuralSource("Unlink", "/p/src/Foo/Bar.res.mjs")).toEqual(false);
17
+ });
18
+ globalThis.test("an add of a .res source is not structural (incremental handles it)", async () => {
19
+ globalThis.expect(Watch$ReventlessGwt.isStructuralSource("Add", "/p/src/Foo/Bar.res")).toEqual(false);
20
+ });
21
+ globalThis.test("a change of a .res source is not structural", async () => {
22
+ globalThis.expect(Watch$ReventlessGwt.isStructuralSource("Change", "/p/src/Foo/Bar.res")).toEqual(false);
23
+ });
24
+ });
25
+
26
+ globalThis.describe("Watch.debounce coalescing", () => {
27
+ globalThis.test("a burst collapses to one call carrying the strongest event", async () => {
28
+ let calls = {
29
+ contents: []
30
+ };
31
+ let fire = Watch$ReventlessGwt.debounce(40, (e, p) => {
32
+ calls.contents = calls.contents.concat([[
33
+ e,
34
+ p
35
+ ]]);
36
+ });
37
+ fire("Change", "/p/src/A.res");
38
+ fire("Add", "/p/src/new/A.res");
39
+ fire("Unlink", "/p/src/old/A.res");
40
+ await delay(120);
41
+ globalThis.expect(calls.contents.length).toEqual(1);
42
+ let match = calls.contents[0];
43
+ globalThis.expect(match[0]).toEqual("Unlink");
44
+ globalThis.expect(match[1]).toEqual("/p/src/old/A.res");
45
+ });
46
+ globalThis.test("a later weaker event does not mask an earlier unlink", async () => {
47
+ let calls = {
48
+ contents: []
49
+ };
50
+ let fire = Watch$ReventlessGwt.debounce(40, (e, _p) => {
51
+ calls.contents = calls.contents.concat([e]);
52
+ });
53
+ fire("Unlink", "/p/src/old/A.res");
54
+ fire("Change", "/p/src/A.res");
55
+ await delay(120);
56
+ globalThis.expect(calls.contents).toEqual(["Unlink"]);
57
+ });
58
+ globalThis.test("interleaved .res.mjs unlinks don't steal the representative path", async () => {
59
+ let calls = {
60
+ contents: []
61
+ };
62
+ let fire = Watch$ReventlessGwt.debounce(40, (e, p) => {
63
+ calls.contents = calls.contents.concat([[
64
+ e,
65
+ p
66
+ ]]);
67
+ });
68
+ fire("Unlink", "/p/src/old/A.res");
69
+ fire("Unlink", "/p/src/old/A.res.mjs");
70
+ fire("Unlink", "/p/src/old/A_Behavior.res");
71
+ fire("Unlink", "/p/src/old/A_Behavior.res.mjs");
72
+ fire("Add", "/p/src/new/A.res");
73
+ fire("Add", "/p/src/new/A.res.mjs");
74
+ await delay(120);
75
+ globalThis.expect(calls.contents.length).toEqual(1);
76
+ let match = calls.contents[0];
77
+ let e = match[0];
78
+ globalThis.expect(e).toEqual("Unlink");
79
+ globalThis.expect(Watch$ReventlessGwt.isStructuralSource(e, match[1])).toEqual(true);
80
+ });
81
+ globalThis.test("a lone change stays a change (incremental re-run)", async () => {
82
+ let calls = {
83
+ contents: []
84
+ };
85
+ let fire = Watch$ReventlessGwt.debounce(40, (e, _p) => {
86
+ calls.contents = calls.contents.concat([e]);
87
+ });
88
+ fire("Change", "/p/src/A.res");
89
+ await delay(120);
90
+ globalThis.expect(calls.contents).toEqual(["Change"]);
91
+ });
92
+ });
93
+
94
+ export {
95
+ delay,
96
+ }
97
+ /* Not a pure module */