@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.
- package/CHANGELOG.md +698 -0
- package/LICENSE +202 -0
- package/bin/reventless-dev.mjs +21 -0
- package/package.json +54 -0
- package/rescript.json +36 -0
- package/src/.gwtignore +10 -0
- package/src/Automation_GWT.res +207 -0
- package/src/Automation_GWT.res.mjs +160 -0
- package/src/Behavior_GWT.res +603 -0
- package/src/Behavior_GWT.res.mjs +426 -0
- package/src/Bind.res +29 -0
- package/src/Bind.res.mjs +22 -0
- package/src/BuildClassifier.res +137 -0
- package/src/BuildClassifier.res.mjs +136 -0
- package/src/Cancellation.res +31 -0
- package/src/Cancellation.res.mjs +45 -0
- package/src/ChildProcess.res +84 -0
- package/src/ChildProcess.res.mjs +92 -0
- package/src/Cli.res +671 -0
- package/src/Cli.res.mjs +754 -0
- package/src/Collector.res +201 -0
- package/src/Collector.res.mjs +215 -0
- package/src/ComponentMeta.res +104 -0
- package/src/ComponentMeta.res.mjs +120 -0
- package/src/ComponentScan.res +103 -0
- package/src/ComponentScan.res.mjs +105 -0
- package/src/Delegate_GWT.res +303 -0
- package/src/Delegate_GWT.res.mjs +372 -0
- package/src/Diff.res +91 -0
- package/src/Diff.res.mjs +98 -0
- package/src/Discovery.res +105 -0
- package/src/Discovery.res.mjs +103 -0
- package/src/DomainDeadCode.res +93 -0
- package/src/DomainDeadCode.res.mjs +69 -0
- package/src/DomainGraph.res +0 -0
- package/src/DomainGraph.res.mjs +0 -0
- package/src/EventMapping_GWT.res +29 -0
- package/src/EventMapping_GWT.res.mjs +44 -0
- package/src/Filter.res +36 -0
- package/src/Filter.res.mjs +40 -0
- package/src/Flow_GWT.res +664 -0
- package/src/Flow_GWT.res.mjs +604 -0
- package/src/FormatterHuman.res +138 -0
- package/src/FormatterHuman.res.mjs +122 -0
- package/src/FormatterJson.res +257 -0
- package/src/FormatterJson.res.mjs +320 -0
- package/src/FormatterJunit.res +93 -0
- package/src/FormatterJunit.res.mjs +90 -0
- package/src/FormatterTap.res +139 -0
- package/src/FormatterTap.res.mjs +149 -0
- package/src/FormatterVsCode.res +362 -0
- package/src/FormatterVsCode.res.mjs +564 -0
- package/src/Hint.res +119 -0
- package/src/Hint.res.mjs +124 -0
- package/src/InboundTranslation_GWT.res +83 -0
- package/src/InboundTranslation_GWT.res.mjs +94 -0
- package/src/JestBind.res +70 -0
- package/src/JestBind.res.mjs +63 -0
- package/src/Loader.res +24 -0
- package/src/Loader.res.mjs +23 -0
- package/src/LocalHost.res +135 -0
- package/src/LocalHost.res.mjs +127 -0
- package/src/Mapping_GWT.res +474 -0
- package/src/Mapping_GWT.res.mjs +322 -0
- package/src/MultiSourceProjection_GWT.res +316 -0
- package/src/MultiSourceProjection_GWT.res.mjs +354 -0
- package/src/OutboundTranslation_GWT.res +238 -0
- package/src/OutboundTranslation_GWT.res.mjs +186 -0
- package/src/Outcome.res +161 -0
- package/src/Outcome.res.mjs +140 -0
- package/src/PackageScan.res +80 -0
- package/src/PackageScan.res.mjs +81 -0
- package/src/PlatformRunner.res +160 -0
- package/src/PlatformRunner.res.mjs +155 -0
- package/src/PlatformScan.res +138 -0
- package/src/PlatformScan.res.mjs +131 -0
- package/src/ProcessManager.res +91 -0
- package/src/ProcessManager.res.mjs +80 -0
- package/src/Projection_GWT.res +312 -0
- package/src/Projection_GWT.res.mjs +332 -0
- package/src/Query_GWT.res +379 -0
- package/src/Query_GWT.res.mjs +367 -0
- package/src/RenderRescript.res +125 -0
- package/src/RenderRescript.res.mjs +141 -0
- package/src/RunnerTypes.res +68 -0
- package/src/RunnerTypes.res.mjs +60 -0
- package/src/SideEffect_GWT.res +157 -0
- package/src/SideEffect_GWT.res.mjs +113 -0
- package/src/StateChange/ExternalAddCategorySlice.res +25 -0
- package/src/StateChange/ExternalAddCategorySlice.res.mjs +34 -0
- package/src/StateChange/ExternalAddCategorySlice_Behavior.res +19 -0
- package/src/StateChange/ExternalAddCategorySlice_Behavior.res.mjs +46 -0
- package/src/StateChange/WithFixtures.res +22 -0
- package/src/StateChange/WithFixtures.res.mjs +31 -0
- package/src/StateChange/WithFixtures_Behavior.res +17 -0
- package/src/StateChange/WithFixtures_Behavior.res.mjs +37 -0
- package/src/StateChange/WithManualOpen.res +23 -0
- package/src/StateChange/WithManualOpen.res.mjs +31 -0
- package/src/StateChange/WithManualOpen_Behavior.res +17 -0
- package/src/StateChange/WithManualOpen_Behavior.res.mjs +37 -0
- package/src/StubRuntime.res +53 -0
- package/src/StubRuntime.res.mjs +54 -0
- package/src/TestFixtures.res +16 -0
- package/src/TestFixtures.res.mjs +34 -0
- package/src/Watch.res +92 -0
- package/src/Watch.res.mjs +83 -0
- package/src/WatcherProbe.res +48 -0
- package/src/WatcherProbe.res.mjs +46 -0
- package/tests/AutomationGwtTest.res +71 -0
- package/tests/AutomationGwtTest.res.mjs +189 -0
- package/tests/BuildClassifierTest.res +94 -0
- package/tests/BuildClassifierTest.res.mjs +132 -0
- package/tests/ComponentMetaTest.res +52 -0
- package/tests/ComponentMetaTest.res.mjs +57 -0
- package/tests/DelegateGwtTest.res +487 -0
- package/tests/DelegateGwtTest.res.mjs +891 -0
- package/tests/DiscoveryTest.res +42 -0
- package/tests/DiscoveryTest.res.mjs +43 -0
- package/tests/DomainDeadCodeTest.res +116 -0
- package/tests/DomainDeadCodeTest.res.mjs +155 -0
- package/tests/DomainGraphTest.res +365 -0
- package/tests/DomainGraphTest.res.mjs +413 -0
- package/tests/FlowAggregateGwtTest.res +169 -0
- package/tests/FlowAggregateGwtTest.res.mjs +269 -0
- package/tests/FlowCrossPluginGwtTest.res +340 -0
- package/tests/FlowCrossPluginGwtTest.res.mjs +798 -0
- package/tests/FlowGwtTest.res +222 -0
- package/tests/FlowGwtTest.res.mjs +432 -0
- package/tests/FormatterVsCodeMessageTest.res +41 -0
- package/tests/FormatterVsCodeMessageTest.res.mjs +42 -0
- package/tests/InboundTranslationGwtTest.res +34 -0
- package/tests/InboundTranslationGwtTest.res.mjs +98 -0
- package/tests/LocalHostIntegration.res +70 -0
- package/tests/LocalHostIntegration.res.mjs +64 -0
- package/tests/LocalHostTest.res +71 -0
- package/tests/LocalHostTest.res.mjs +63 -0
- package/tests/MappingGwtTest.res +348 -0
- package/tests/MappingGwtTest.res.mjs +592 -0
- package/tests/OutboundTranslationGwtTest.res +59 -0
- package/tests/OutboundTranslationGwtTest.res.mjs +138 -0
- package/tests/PlatformRunnerTest.res +66 -0
- package/tests/PlatformRunnerTest.res.mjs +85 -0
- package/tests/PlatformScanTest.res +61 -0
- package/tests/PlatformScanTest.res.mjs +56 -0
- package/tests/QueryGwtTest.res +161 -0
- package/tests/QueryGwtTest.res.mjs +314 -0
- package/tests/QueryResolverGwtTest.res +71 -0
- package/tests/QueryResolverGwtTest.res.mjs +136 -0
- package/tests/RunnerUnitTest.res +219 -0
- package/tests/RunnerUnitTest.res.mjs +244 -0
- package/tests/SideEffect/FakeOrderNotification.res +23 -0
- package/tests/SideEffect/FakeOrderNotification.res.mjs +36 -0
- package/tests/SideEffect/FakeOrderNotificationGwtTest.res +35 -0
- package/tests/SideEffect/FakeOrderNotificationGwtTest.res.mjs +73 -0
- package/tests/SideEffect/MockEmail.res +18 -0
- package/tests/SideEffect/MockEmail.res.mjs +47 -0
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res +27 -0
- package/tests/StateChange/ExternalAddCategorySlice_GWT.res.mjs +70 -0
- package/tests/StateChange/WithFixtures_Fixtures.res +9 -0
- package/tests/StateChange/WithFixtures_Fixtures.res.mjs +20 -0
- package/tests/StateChange/WithFixtures_GWT.res +23 -0
- package/tests/StateChange/WithFixtures_GWT.res.mjs +56 -0
- package/tests/StateChange/WithManualOpen_Fixtures.res +7 -0
- package/tests/StateChange/WithManualOpen_Fixtures.res.mjs +20 -0
- package/tests/StateChange/WithManualOpen_GWT.res +17 -0
- package/tests/StateChange/WithManualOpen_GWT.res.mjs +56 -0
- package/tests/StateChangeSliceGwtTest.res +160 -0
- package/tests/StateChangeSliceGwtTest.res.mjs +280 -0
- package/tests/StateViewSliceGwtTest.res +64 -0
- package/tests/StateViewSliceGwtTest.res.mjs +161 -0
- package/tests/WatchDebounceTest.res +78 -0
- package/tests/WatchDebounceTest.res.mjs +97 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Inert runtime arguments shared by the Delegate / Flow GWT DSLs.
|
|
2
|
+
//
|
|
3
|
+
// ExtensionPoint mappings and Extension delegates are pure translations, but
|
|
4
|
+
// their user-level functions take infrastructure handles (a query engine, a
|
|
5
|
+
// scheduler, message metadata, the plugin definition). The GWT runs them with
|
|
6
|
+
// these no-op stubs so the translation logic is exercised without any real
|
|
7
|
+
// infrastructure. See `docs/plans/done/gwt-flow-and-extension-test-kinds.md` Phase 0.
|
|
8
|
+
|
|
9
|
+
// No-op QueryEngine — any DB lookup resolves to empty results. Kept identical
|
|
10
|
+
// to the inline stub `Mapping_GWT` defines.
|
|
11
|
+
let queryEngine: Reventless.QueryEngine.operations = {
|
|
12
|
+
scan: (~readModelName as _, ~filterConfigs as _, ~limit as _) => []->Promise.resolve,
|
|
13
|
+
query: (
|
|
14
|
+
~readModelName as _: string,
|
|
15
|
+
~key as _: option<string>=?,
|
|
16
|
+
~id as _: Reventless.QueryEngine.value,
|
|
17
|
+
~subIdConfig as _: option<Reventless.QueryEngine.SubId.config>=?,
|
|
18
|
+
~filterConfigs as _: option<array<Reventless.QueryEngine.Filter.config>>=?,
|
|
19
|
+
~ascending as _: option<bool>=?,
|
|
20
|
+
~limit as _: option<int>=?,
|
|
21
|
+
) => []->Promise.resolve,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Scheduler stubs — `create` / `delete` are inert. EP/Extension `Call`
|
|
25
|
+
// directives receive these as the side-effect handles they never act on.
|
|
26
|
+
let createSchedule: Reventless.Schedule.create = _schedule => Promise.resolve()
|
|
27
|
+
let deleteSchedule: Reventless.Schedule.delete = _name => Promise.resolve()
|
|
28
|
+
|
|
29
|
+
// Default message metadata threaded into the user-level mapping functions.
|
|
30
|
+
let meta: Reventless.Message.meta = {
|
|
31
|
+
service: "gwt",
|
|
32
|
+
time: "1970-01-01T00:00:00Z",
|
|
33
|
+
msgId: "gwt-msg",
|
|
34
|
+
correlationId: "gwt-corr",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Minimal plugin definition — Extension delegates receive it as an inert arg.
|
|
38
|
+
// Mirrors the `fakePluginDefinition` the platform admin uses for its internal
|
|
39
|
+
// callback wiring.
|
|
40
|
+
let pluginDefinition: Reventless.Plugin.pluginDefinition = {
|
|
41
|
+
id: "Gwt@TEST",
|
|
42
|
+
name: "Gwt",
|
|
43
|
+
version: "TEST",
|
|
44
|
+
extensionPoints: [],
|
|
45
|
+
extensions: [],
|
|
46
|
+
eventCollector: "NOT-SET",
|
|
47
|
+
extensionProtocols: [],
|
|
48
|
+
apiSchemaFragment: None,
|
|
49
|
+
apiTarget: None,
|
|
50
|
+
uiFragments: None,
|
|
51
|
+
structure: None,
|
|
52
|
+
dcbEventLog: None,
|
|
53
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function queryEngine_scan(param, param$1, param$2) {
|
|
5
|
+
return Promise.resolve([]);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function queryEngine_query(param, param$1, param$2, param$3, param$4, param$5, param$6) {
|
|
9
|
+
return Promise.resolve([]);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let queryEngine = {
|
|
13
|
+
scan: queryEngine_scan,
|
|
14
|
+
query: queryEngine_query
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function createSchedule(_schedule) {
|
|
18
|
+
return Promise.resolve();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function deleteSchedule(_name) {
|
|
22
|
+
return Promise.resolve();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let pluginDefinition = {
|
|
26
|
+
id: "Gwt@TEST",
|
|
27
|
+
name: "Gwt",
|
|
28
|
+
version: "TEST",
|
|
29
|
+
extensionPoints: [],
|
|
30
|
+
extensions: [],
|
|
31
|
+
eventCollector: "NOT-SET",
|
|
32
|
+
extensionProtocols: [],
|
|
33
|
+
apiSchemaFragment: undefined,
|
|
34
|
+
apiTarget: undefined,
|
|
35
|
+
uiFragments: undefined,
|
|
36
|
+
structure: undefined,
|
|
37
|
+
dcbEventLog: undefined
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
let meta = {
|
|
41
|
+
service: "gwt",
|
|
42
|
+
time: "1970-01-01T00:00:00Z",
|
|
43
|
+
msgId: "gwt-msg",
|
|
44
|
+
correlationId: "gwt-corr"
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
queryEngine,
|
|
49
|
+
createSchedule,
|
|
50
|
+
deleteSchedule,
|
|
51
|
+
meta,
|
|
52
|
+
pluginDefinition,
|
|
53
|
+
}
|
|
54
|
+
/* No side effect */
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
let id = "id"
|
|
2
|
+
let meta = {
|
|
3
|
+
Reventless.Message.service: "service",
|
|
4
|
+
user: "ProjectionTest",
|
|
5
|
+
ip: "ip",
|
|
6
|
+
time: "time",
|
|
7
|
+
msgId: "msgId",
|
|
8
|
+
correlationId: "correlationId",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let context = {Reventless.Message.meta, id}
|
|
12
|
+
|
|
13
|
+
let statusChange = {
|
|
14
|
+
Reventless.Message.at: context.meta.time,
|
|
15
|
+
by: context.meta.user->Option.getOr(""),
|
|
16
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
|
|
5
|
+
let id = "id";
|
|
6
|
+
|
|
7
|
+
let meta = {
|
|
8
|
+
service: "service",
|
|
9
|
+
time: "time",
|
|
10
|
+
ip: "ip",
|
|
11
|
+
user: "ProjectionTest",
|
|
12
|
+
msgId: "msgId",
|
|
13
|
+
correlationId: "correlationId"
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
let context = {
|
|
17
|
+
id: id,
|
|
18
|
+
meta: meta
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
let statusChange_by = Stdlib_Option.getOr("ProjectionTest", "");
|
|
22
|
+
|
|
23
|
+
let statusChange = {
|
|
24
|
+
at: "time",
|
|
25
|
+
by: statusChange_by
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
id,
|
|
30
|
+
meta,
|
|
31
|
+
context,
|
|
32
|
+
statusChange,
|
|
33
|
+
}
|
|
34
|
+
/* statusChange Not a pure module */
|
package/src/Watch.res
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Wraps `chokidar` for the `reventless-gwt watch` and `reventless-gwt run
|
|
2
|
+
// --watch` modes. Emits one callback per file change event (creates, edits,
|
|
3
|
+
// deletes) after a small debounce window so rapid editor saves don't trigger
|
|
4
|
+
// overlapping runs.
|
|
5
|
+
|
|
6
|
+
type watcher
|
|
7
|
+
type opts = {
|
|
8
|
+
ignoreInitial: bool,
|
|
9
|
+
ignored: array<string>,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// The kind of filesystem change, threaded through so the consumer can tell a
|
|
13
|
+
// *structural* change (a source file appearing/disappearing — the move/delete
|
|
14
|
+
// half of a relocation) from a plain edit. Phase 12 keys its clean-rebuild on
|
|
15
|
+
// an `Unlink` of a `.res` source.
|
|
16
|
+
type event = Add | Change | Unlink
|
|
17
|
+
|
|
18
|
+
@module("chokidar") external watch: (array<string>, opts) => watcher = "watch"
|
|
19
|
+
@send external on: (watcher, string, string => unit) => watcher = "on"
|
|
20
|
+
@send external close: watcher => promise<unit> = "close"
|
|
21
|
+
|
|
22
|
+
@val external setTimeout: (unit => unit, int) => float = "setTimeout"
|
|
23
|
+
@val external clearTimeout: float => unit = "clearTimeout"
|
|
24
|
+
|
|
25
|
+
// Rank events so a coalesced burst reports the *strongest structural* signal
|
|
26
|
+
// with a representative path. A single directory `mv` emits an interleaved
|
|
27
|
+
// burst — `unlink` of the `.res` AND the generated `.res.mjs`, then `add`s at
|
|
28
|
+
// the new path — inside one debounce window. The representative path must be a
|
|
29
|
+
// `.res` *source* unlink (the relocation signal), never a `.res.mjs` or a
|
|
30
|
+
// trailing add, or the consumer misclassifies it as an ordinary change. So an
|
|
31
|
+
// unlink of a `.res` source outranks everything; any other unlink/add outranks
|
|
32
|
+
// a plain edit.
|
|
33
|
+
let rank = (event: event, path: string): int =>
|
|
34
|
+
switch event {
|
|
35
|
+
| Unlink => String.endsWith(path, ".res") ? 2 : 1
|
|
36
|
+
| Add => 1
|
|
37
|
+
| Change => 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// True when an event is the *structural* signal Phase 12 acts on: a source
|
|
41
|
+
// `.res` file leaving a directory (the move/delete half of a relocation).
|
|
42
|
+
// Generated `.res.mjs` outputs end in `.mjs`, so they never match — which is
|
|
43
|
+
// also what keeps a `rescript clean` (it deletes those outputs) from
|
|
44
|
+
// re-triggering the clean-rebuild it just ran.
|
|
45
|
+
let isStructuralSource = (event: event, path: string): bool =>
|
|
46
|
+
event == Unlink && String.endsWith(path, ".res")
|
|
47
|
+
|
|
48
|
+
let debounce = (wait: int, fn: (event, string) => unit) => {
|
|
49
|
+
let pending: ref<option<float>> = ref(None)
|
|
50
|
+
let bestRank = ref(-1)
|
|
51
|
+
let bestEvent = ref(Change)
|
|
52
|
+
let bestPath = ref("")
|
|
53
|
+
(event, path) => {
|
|
54
|
+
let r = rank(event, path)
|
|
55
|
+
if r >= bestRank.contents {
|
|
56
|
+
bestRank := r
|
|
57
|
+
bestEvent := event
|
|
58
|
+
bestPath := path
|
|
59
|
+
}
|
|
60
|
+
switch pending.contents {
|
|
61
|
+
| Some(t) => clearTimeout(t)
|
|
62
|
+
| None => ()
|
|
63
|
+
}
|
|
64
|
+
pending :=
|
|
65
|
+
Some(
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
pending := None
|
|
68
|
+
let e = bestEvent.contents
|
|
69
|
+
let p = bestPath.contents
|
|
70
|
+
bestRank := -1
|
|
71
|
+
bestEvent := Change
|
|
72
|
+
bestPath := ""
|
|
73
|
+
fn(e, p)
|
|
74
|
+
}, wait),
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let start = (roots: array<string>, onChange: (event, string) => unit): watcher => {
|
|
80
|
+
let w = watch(
|
|
81
|
+
roots,
|
|
82
|
+
{
|
|
83
|
+
ignoreInitial: true,
|
|
84
|
+
ignored: ["**/node_modules/**", "**/lib/**", "**/.git/**"],
|
|
85
|
+
},
|
|
86
|
+
)
|
|
87
|
+
let debounced = debounce(120, onChange)
|
|
88
|
+
w
|
|
89
|
+
->on("add", p => debounced(Add, p))
|
|
90
|
+
->on("change", p => debounced(Change, p))
|
|
91
|
+
->on("unlink", p => debounced(Unlink, p))
|
|
92
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Chokidar from "chokidar";
|
|
4
|
+
|
|
5
|
+
function rank(event, path) {
|
|
6
|
+
switch (event) {
|
|
7
|
+
case "Add" :
|
|
8
|
+
return 1;
|
|
9
|
+
case "Change" :
|
|
10
|
+
return 0;
|
|
11
|
+
case "Unlink" :
|
|
12
|
+
if (path.endsWith(".res")) {
|
|
13
|
+
return 2;
|
|
14
|
+
} else {
|
|
15
|
+
return 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isStructuralSource(event, path) {
|
|
21
|
+
if (event === "Unlink") {
|
|
22
|
+
return path.endsWith(".res");
|
|
23
|
+
} else {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function debounce(wait, fn) {
|
|
29
|
+
let pending = {
|
|
30
|
+
contents: undefined
|
|
31
|
+
};
|
|
32
|
+
let bestRank = {
|
|
33
|
+
contents: -1
|
|
34
|
+
};
|
|
35
|
+
let bestEvent = {
|
|
36
|
+
contents: "Change"
|
|
37
|
+
};
|
|
38
|
+
let bestPath = {
|
|
39
|
+
contents: ""
|
|
40
|
+
};
|
|
41
|
+
return (event, path) => {
|
|
42
|
+
let r = rank(event, path);
|
|
43
|
+
if (r >= bestRank.contents) {
|
|
44
|
+
bestRank.contents = r;
|
|
45
|
+
bestEvent.contents = event;
|
|
46
|
+
bestPath.contents = path;
|
|
47
|
+
}
|
|
48
|
+
let t = pending.contents;
|
|
49
|
+
if (t !== undefined) {
|
|
50
|
+
clearTimeout(t);
|
|
51
|
+
}
|
|
52
|
+
pending.contents = setTimeout(() => {
|
|
53
|
+
pending.contents = undefined;
|
|
54
|
+
let e = bestEvent.contents;
|
|
55
|
+
let p = bestPath.contents;
|
|
56
|
+
bestRank.contents = -1;
|
|
57
|
+
bestEvent.contents = "Change";
|
|
58
|
+
bestPath.contents = "";
|
|
59
|
+
fn(e, p);
|
|
60
|
+
}, wait);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function start(roots, onChange) {
|
|
65
|
+
let w = Chokidar.watch(roots, {
|
|
66
|
+
ignoreInitial: true,
|
|
67
|
+
ignored: [
|
|
68
|
+
"**/node_modules/**",
|
|
69
|
+
"**/lib/**",
|
|
70
|
+
"**/.git/**"
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
let debounced = debounce(120, onChange);
|
|
74
|
+
return w.on("add", p => debounced("Add", p)).on("change", p => debounced("Change", p)).on("unlink", p => debounced("Unlink", p));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
rank,
|
|
79
|
+
isStructuralSource,
|
|
80
|
+
debounce,
|
|
81
|
+
start,
|
|
82
|
+
}
|
|
83
|
+
/* chokidar Not a pure module */
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Detects whether a ReScript build watcher is already running for a package, so
|
|
2
|
+
// the watch manager can defer to a developer's (or rescript-vscode's) own
|
|
3
|
+
// watcher rather than spawn a duplicate. ReScript records the building process's
|
|
4
|
+
// PID in a lock file and liveness-checks it (a dead-pid lock is taken over), so
|
|
5
|
+
// the reliable signal is: lock present AND its pid is alive. A stale lock is
|
|
6
|
+
// harmless — rescript self-heals it on the next build.
|
|
7
|
+
//
|
|
8
|
+
// Two lock files exist depending on which command form started the build:
|
|
9
|
+
// - `lib/watch.lock` — canonical `rescript watch` (used by rescript-vscode
|
|
10
|
+
// ≥ 1.73's "Start Build", and the non-deprecated form)
|
|
11
|
+
// - `lib/rescript.lock` — legacy `rescript build -w` (what `pnpm run start`
|
|
12
|
+
// still maps to in the example packages)
|
|
13
|
+
// Both must be checked: missing `watch.lock` is exactly how the rescript-vscode
|
|
14
|
+
// watcher slipped past an earlier rescript.lock-only probe, causing a doomed
|
|
15
|
+
// duplicate spawn.
|
|
16
|
+
|
|
17
|
+
@module("node:fs") external _existsSync: string => bool = "existsSync"
|
|
18
|
+
@module("node:fs") external _readFileSync: (string, string) => string = "readFileSync"
|
|
19
|
+
@module("node:path") external join: (string, string) => string = "join"
|
|
20
|
+
@val external _processKillSig: (int, int) => unit = "process.kill"
|
|
21
|
+
|
|
22
|
+
// `process.kill(pid, 0)` performs no signal delivery — it only probes existence
|
|
23
|
+
// (throws ESRCH when the pid is gone), which is exactly the liveness check.
|
|
24
|
+
let isAlive = (pid: int): bool =>
|
|
25
|
+
try {
|
|
26
|
+
_processKillSig(pid, 0)
|
|
27
|
+
true
|
|
28
|
+
} catch {
|
|
29
|
+
| _ => false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let libLock = (dir: string, name: string): string => join(join(dir, "lib"), name)
|
|
33
|
+
|
|
34
|
+
// True when the named lock file exists and holds a live pid.
|
|
35
|
+
let lockHasLivePid = (lock: string): bool =>
|
|
36
|
+
if _existsSync(lock) {
|
|
37
|
+
switch Int.fromString(String.trim(_readFileSync(lock, "utf8"))) {
|
|
38
|
+
| Some(pid) => isAlive(pid)
|
|
39
|
+
| None => false
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// True when an external watcher (either command form) already owns this
|
|
46
|
+
// package's build.
|
|
47
|
+
let hasLiveWatcher = (dir: string): bool =>
|
|
48
|
+
lockHasLivePid(libLock(dir, "watch.lock")) || lockHasLivePid(libLock(dir, "rescript.lock"))
|
|
@@ -0,0 +1,46 @@
|
|
|
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_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
6
|
+
|
|
7
|
+
function isAlive(pid) {
|
|
8
|
+
try {
|
|
9
|
+
process.kill(pid, 0);
|
|
10
|
+
return true;
|
|
11
|
+
} catch (exn) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function libLock(dir, name) {
|
|
17
|
+
return Nodepath.join(Nodepath.join(dir, "lib"), name);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function lockHasLivePid(lock) {
|
|
21
|
+
if (!Nodefs.existsSync(lock)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
let pid = Stdlib_Int.fromString(Nodefs.readFileSync(lock, "utf8").trim(), undefined);
|
|
25
|
+
if (pid !== undefined) {
|
|
26
|
+
return isAlive(pid);
|
|
27
|
+
} else {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function hasLiveWatcher(dir) {
|
|
33
|
+
if (lockHasLivePid(libLock(dir, "watch.lock"))) {
|
|
34
|
+
return true;
|
|
35
|
+
} else {
|
|
36
|
+
return lockHasLivePid(libLock(dir, "rescript.lock"));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
isAlive,
|
|
42
|
+
libLock,
|
|
43
|
+
lockHasLivePid,
|
|
44
|
+
hasLiveWatcher,
|
|
45
|
+
}
|
|
46
|
+
/* node:fs Not a pure module */
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Worked example for AutomationSlice_GWT — a "ship order" automation:
|
|
2
|
+
// OrderPlaced ⇒ TODO, ShipmentCreated resolves the TODO, process emits
|
|
3
|
+
// CreateShipment. Covers the unit combinators (collect / resolve / process)
|
|
4
|
+
// and the scenario combinators (sweep / andThenEvents).
|
|
5
|
+
|
|
6
|
+
@@reventless.gwt
|
|
7
|
+
|
|
8
|
+
module ShipOrderSlice = {
|
|
9
|
+
let name = "ShipOrder"
|
|
10
|
+
|
|
11
|
+
@schema
|
|
12
|
+
type consumedEvent =
|
|
13
|
+
| OrderPlaced({orderId: string, shippingAddress: string})
|
|
14
|
+
| ShipmentCreated({orderId: string})
|
|
15
|
+
|
|
16
|
+
@schema
|
|
17
|
+
type todoItem = {orderId: string, shippingAddress: string}
|
|
18
|
+
|
|
19
|
+
@schema
|
|
20
|
+
type command = CreateShipment({orderId: string, address: string})
|
|
21
|
+
|
|
22
|
+
let collect = event =>
|
|
23
|
+
switch event {
|
|
24
|
+
| OrderPlaced({orderId, shippingAddress}) => [(orderId, {orderId, shippingAddress})]
|
|
25
|
+
| _ => []
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let resolve = event =>
|
|
29
|
+
switch event {
|
|
30
|
+
| ShipmentCreated({orderId}) => Some(orderId)
|
|
31
|
+
| _ => None
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let process = (_id, item) =>
|
|
35
|
+
Some((item.orderId, CreateShipment({orderId: item.orderId, address: item.shippingAddress})))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("ShipOrder AutomationSlice", () => {
|
|
39
|
+
test("collect: OrderPlaced creates a pending TODO", () =>
|
|
40
|
+
givenEvent(OrderPlaced({orderId: "o1", shippingAddress: "1 Main St"}))
|
|
41
|
+
->whenCollect
|
|
42
|
+
->thenTodos([("o1", {orderId: "o1", shippingAddress: "1 Main St"})])
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
test("resolve: ShipmentCreated marks the TODO done", () =>
|
|
46
|
+
givenEvent(ShipmentCreated({orderId: "o1"}))
|
|
47
|
+
->whenResolve
|
|
48
|
+
->thenResolved(Some("o1"))
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
test("process: pending TODO emits CreateShipment", () =>
|
|
52
|
+
givenTodo("o1", {orderId: "o1", shippingAddress: "1 Main St"})
|
|
53
|
+
->whenProcess
|
|
54
|
+
->thenCommand("o1", CreateShipment({orderId: "o1", address: "1 Main St"}))
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
test("sweep: events → commands, andThenEvents drains todos", () => {
|
|
58
|
+
let s =
|
|
59
|
+
givenEvents([OrderPlaced({orderId: "o1", shippingAddress: "1 Main St"})])
|
|
60
|
+
->whenSweep
|
|
61
|
+
let commandsOk =
|
|
62
|
+
thenCommands(s, [("o1", CreateShipment({orderId: "o1", address: "1 Main St"}))])
|
|
63
|
+
let drained = andThenEvents(s, [ShipmentCreated({orderId: "o1"})])
|
|
64
|
+
let todosOk = thenScenarioTodos(drained, [])
|
|
65
|
+
switch (commandsOk, todosOk) {
|
|
66
|
+
| (Ok(), Ok()) => Outcome.pass
|
|
67
|
+
| (Error(_) as e, _) => e
|
|
68
|
+
| (_, Error(_) as e) => e
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Outcome$ReventlessGwt from "../src/Outcome.res.mjs";
|
|
5
|
+
import * as Automation_GWT$ReventlessGwt from "../src/Automation_GWT.res.mjs";
|
|
6
|
+
|
|
7
|
+
let consumedEventSchema = S.union([
|
|
8
|
+
S.schema(s => ({
|
|
9
|
+
TAG: "OrderPlaced",
|
|
10
|
+
orderId: s.m(S.string),
|
|
11
|
+
shippingAddress: s.m(S.string)
|
|
12
|
+
})),
|
|
13
|
+
S.schema(s => ({
|
|
14
|
+
TAG: "ShipmentCreated",
|
|
15
|
+
orderId: s.m(S.string)
|
|
16
|
+
}))
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
let todoItemSchema = S.schema(s => ({
|
|
20
|
+
orderId: s.m(S.string),
|
|
21
|
+
shippingAddress: s.m(S.string)
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
let commandSchema = S.schema(s => ({
|
|
25
|
+
TAG: "CreateShipment",
|
|
26
|
+
orderId: s.m(S.string),
|
|
27
|
+
address: s.m(S.string)
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
function collect(event) {
|
|
31
|
+
if (event.TAG !== "OrderPlaced") {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
let orderId = event.orderId;
|
|
35
|
+
return [[
|
|
36
|
+
orderId,
|
|
37
|
+
{
|
|
38
|
+
orderId: orderId,
|
|
39
|
+
shippingAddress: event.shippingAddress
|
|
40
|
+
}
|
|
41
|
+
]];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolve(event) {
|
|
45
|
+
if (event.TAG === "OrderPlaced") {
|
|
46
|
+
return;
|
|
47
|
+
} else {
|
|
48
|
+
return event.orderId;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function process(_id, item) {
|
|
53
|
+
return [
|
|
54
|
+
item.orderId,
|
|
55
|
+
{
|
|
56
|
+
TAG: "CreateShipment",
|
|
57
|
+
orderId: item.orderId,
|
|
58
|
+
address: item.shippingAddress
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function commandAuthorization(param) {
|
|
64
|
+
return "AllowAuthenticated";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let ShipOrderSlice = {
|
|
68
|
+
name: "ShipOrder",
|
|
69
|
+
consumedEventSchema: consumedEventSchema,
|
|
70
|
+
todoItemSchema: todoItemSchema,
|
|
71
|
+
commandSchema: commandSchema,
|
|
72
|
+
collect: collect,
|
|
73
|
+
resolve: resolve,
|
|
74
|
+
process: process,
|
|
75
|
+
commandAuthorization: commandAuthorization
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
let include = Automation_GWT$ReventlessGwt.Make(ShipOrderSlice);
|
|
79
|
+
|
|
80
|
+
let describe = include.describe;
|
|
81
|
+
|
|
82
|
+
let test = include.test;
|
|
83
|
+
|
|
84
|
+
let givenEvent = include.givenEvent;
|
|
85
|
+
|
|
86
|
+
let whenCollect = include.whenCollect;
|
|
87
|
+
|
|
88
|
+
let thenTodos = include.thenTodos;
|
|
89
|
+
|
|
90
|
+
let whenResolve = include.whenResolve;
|
|
91
|
+
|
|
92
|
+
let thenResolved = include.thenResolved;
|
|
93
|
+
|
|
94
|
+
let givenTodo = include.givenTodo;
|
|
95
|
+
|
|
96
|
+
let whenProcess = include.whenProcess;
|
|
97
|
+
|
|
98
|
+
let thenCommand = include.thenCommand;
|
|
99
|
+
|
|
100
|
+
let givenEvents = include.givenEvents;
|
|
101
|
+
|
|
102
|
+
let whenSweep = include.whenSweep;
|
|
103
|
+
|
|
104
|
+
let thenCommands = include.thenCommands;
|
|
105
|
+
|
|
106
|
+
let andThenEvents = include.andThenEvents;
|
|
107
|
+
|
|
108
|
+
let thenScenarioTodos = include.thenScenarioTodos;
|
|
109
|
+
|
|
110
|
+
describe("ShipOrder AutomationSlice", () => {
|
|
111
|
+
test("collect: OrderPlaced creates a pending TODO", () => thenTodos(whenCollect(givenEvent({
|
|
112
|
+
TAG: "OrderPlaced",
|
|
113
|
+
orderId: "o1",
|
|
114
|
+
shippingAddress: "1 Main St"
|
|
115
|
+
})), [[
|
|
116
|
+
"o1",
|
|
117
|
+
{
|
|
118
|
+
orderId: "o1",
|
|
119
|
+
shippingAddress: "1 Main St"
|
|
120
|
+
}
|
|
121
|
+
]]));
|
|
122
|
+
test("resolve: ShipmentCreated marks the TODO done", () => thenResolved(whenResolve(givenEvent({
|
|
123
|
+
TAG: "ShipmentCreated",
|
|
124
|
+
orderId: "o1"
|
|
125
|
+
})), "o1"));
|
|
126
|
+
test("process: pending TODO emits CreateShipment", () => thenCommand(whenProcess(givenTodo("o1", {
|
|
127
|
+
orderId: "o1",
|
|
128
|
+
shippingAddress: "1 Main St"
|
|
129
|
+
})), "o1", {
|
|
130
|
+
TAG: "CreateShipment",
|
|
131
|
+
orderId: "o1",
|
|
132
|
+
address: "1 Main St"
|
|
133
|
+
}));
|
|
134
|
+
test("sweep: events → commands, andThenEvents drains todos", () => {
|
|
135
|
+
let s = whenSweep(givenEvents([{
|
|
136
|
+
TAG: "OrderPlaced",
|
|
137
|
+
orderId: "o1",
|
|
138
|
+
shippingAddress: "1 Main St"
|
|
139
|
+
}]));
|
|
140
|
+
let commandsOk = thenCommands(s, [[
|
|
141
|
+
"o1",
|
|
142
|
+
{
|
|
143
|
+
TAG: "CreateShipment",
|
|
144
|
+
orderId: "o1",
|
|
145
|
+
address: "1 Main St"
|
|
146
|
+
}
|
|
147
|
+
]]);
|
|
148
|
+
let drained = andThenEvents(s, [{
|
|
149
|
+
TAG: "ShipmentCreated",
|
|
150
|
+
orderId: "o1"
|
|
151
|
+
}]);
|
|
152
|
+
let todosOk = thenScenarioTodos(drained, []);
|
|
153
|
+
if (commandsOk.TAG === "Ok") {
|
|
154
|
+
if (todosOk.TAG === "Ok") {
|
|
155
|
+
return Outcome$ReventlessGwt.pass;
|
|
156
|
+
} else {
|
|
157
|
+
return todosOk;
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
return commandsOk;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
let Spec = include.Spec;
|
|
166
|
+
|
|
167
|
+
let thenNoCommand = include.thenNoCommand;
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
ShipOrderSlice,
|
|
171
|
+
Spec,
|
|
172
|
+
describe,
|
|
173
|
+
test,
|
|
174
|
+
givenEvent,
|
|
175
|
+
whenCollect,
|
|
176
|
+
thenTodos,
|
|
177
|
+
whenResolve,
|
|
178
|
+
thenResolved,
|
|
179
|
+
givenTodo,
|
|
180
|
+
whenProcess,
|
|
181
|
+
thenCommand,
|
|
182
|
+
thenNoCommand,
|
|
183
|
+
givenEvents,
|
|
184
|
+
whenSweep,
|
|
185
|
+
thenCommands,
|
|
186
|
+
andThenEvents,
|
|
187
|
+
thenScenarioTodos,
|
|
188
|
+
}
|
|
189
|
+
/* consumedEventSchema Not a pure module */
|