@reventlessdev/reventless-gwt 1.0.0-alpha.76 → 1.0.0-alpha.77

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 (45) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +6 -6
  3. package/src/Behavior_GWT.res +125 -159
  4. package/src/Behavior_GWT.res.mjs +156 -59
  5. package/src/BuildClassifier.res +15 -5
  6. package/src/BuildClassifier.res.mjs +3 -3
  7. package/src/Cli.res +157 -29
  8. package/src/Cli.res.mjs +138 -24
  9. package/src/Collector.res +2 -2
  10. package/src/Collector.res.mjs +2 -2
  11. package/src/ComponentMeta.res +16 -43
  12. package/src/ComponentMeta.res.mjs +12 -41
  13. package/src/Discovery.res +19 -19
  14. package/src/Discovery.res.mjs +5 -9
  15. package/src/DomainGraph.res +0 -0
  16. package/src/DomainGraph.res.mjs +0 -0
  17. package/src/FormatterHuman.res +14 -43
  18. package/src/FormatterHuman.res.mjs +21 -28
  19. package/src/FormatterJson.res +12 -5
  20. package/src/FormatterJson.res.mjs +8 -6
  21. package/src/FormatterTap.res +13 -52
  22. package/src/FormatterTap.res.mjs +25 -43
  23. package/src/FormatterVsCode.res +4 -19
  24. package/src/FormatterVsCode.res.mjs +4 -23
  25. package/src/LocalHost.res +11 -27
  26. package/src/LocalHost.res.mjs +6 -34
  27. package/src/MismatchRender.res +72 -0
  28. package/src/MismatchRender.res.mjs +127 -0
  29. package/src/Outcome.res +0 -64
  30. package/src/Outcome.res.mjs +0 -47
  31. package/src/PlatformScan.res +14 -10
  32. package/src/PlatformScan.res.mjs +14 -13
  33. package/tests/BuildClassifierTest.res +39 -0
  34. package/tests/BuildClassifierTest.res.mjs +44 -0
  35. package/tests/CliWatchScopeTest.res +62 -0
  36. package/tests/CliWatchScopeTest.res.mjs +95 -0
  37. package/tests/ComponentMetaTest.res +15 -0
  38. package/tests/ComponentMetaTest.res.mjs +14 -0
  39. package/tests/DomainGraphTest.res +2 -2
  40. package/tests/FlowAggregateGwtTest.res +2 -0
  41. package/tests/FlowAggregateGwtTest.res.mjs +4 -0
  42. package/tests/FormatterGoldenTest.res +125 -0
  43. package/tests/FormatterGoldenTest.res.mjs +162 -0
  44. package/tests/MappingGwtTest.res +2 -0
  45. package/tests/MappingGwtTest.res.mjs +4 -0
@@ -2,8 +2,7 @@
2
2
 
3
3
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
4
  import * as Hint$ReventlessGwt from "./Hint.res.mjs";
5
- import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
6
- import * as RenderRescript$ReventlessGwt from "./RenderRescript.res.mjs";
5
+ import * as MismatchRender$ReventlessGwt from "./MismatchRender.res.mjs";
7
6
 
8
7
  function write(s) {
9
8
  process.stdout.write(s);
@@ -27,47 +26,30 @@ function yamlLine(indentOpt, key, value) {
27
26
  }
28
27
 
29
28
  function renderMismatchYaml(m) {
30
- let lines = [];
31
- lines.push(yamlLine(undefined, "kind", yamlString(Outcome$ReventlessGwt.kindName(m))));
32
- switch (m.TAG) {
33
- case "ErrorMismatch" :
34
- let actual = m.actual;
35
- lines.push(yamlLine(undefined, "expected", yamlString("Error(" + RenderRescript$ReventlessGwt.render(undefined, m.expected) + ")")));
36
- let actualStr = actual !== undefined ? "Error(" + RenderRescript$ReventlessGwt.render(undefined, actual) + ")" : "Ok(" + RenderRescript$ReventlessGwt.renderMany(m.actualEvents) + ")";
37
- lines.push(yamlLine(undefined, "actual", yamlString(actualStr)));
38
- break;
39
- case "StateMismatch" :
40
- lines.push(yamlLine(undefined, "key", yamlString(m.key)));
41
- lines.push(yamlLine(undefined, "expected", yamlString(RenderRescript$ReventlessGwt.renderOption(m.expected))));
42
- lines.push(yamlLine(undefined, "actual", yamlString(RenderRescript$ReventlessGwt.renderOption(m.actual))));
43
- break;
44
- case "NoEventExpected" :
45
- lines.push(yamlLine(undefined, "actual", yamlString(RenderRescript$ReventlessGwt.renderMany(m.actual))));
46
- break;
47
- case "TodoMismatch" :
48
- let fmt = arr => arr.map(param => "(" + param[0] + ", " + RenderRescript$ReventlessGwt.render(undefined, param[1]) + ")").join(", ");
49
- lines.push(yamlLine(undefined, "expected", yamlString("[" + fmt(m.expected) + "]")));
50
- lines.push(yamlLine(undefined, "actual", yamlString("[" + fmt(m.actual) + "]")));
51
- break;
52
- case "AppendConditionMismatch" :
53
- lines.push(yamlLine(undefined, "expected", yamlString(RenderRescript$ReventlessGwt.render(undefined, m.expected))));
54
- lines.push(yamlLine(undefined, "actual", yamlString(RenderRescript$ReventlessGwt.render(undefined, m.actual))));
55
- break;
56
- case "TranslateError" :
57
- lines.push(yamlLine(undefined, "expected", yamlString(m.expected)));
58
- lines.push(yamlLine(undefined, "actual", yamlString(Stdlib_Option.getOr(m.actual, "(none)"))));
59
- break;
60
- case "EventsMismatch" :
61
- case "QueryRowsMismatch" :
62
- case "PublishedActionsMismatch" :
63
- lines.push(yamlLine(undefined, "expected", yamlString(RenderRescript$ReventlessGwt.renderMany(m.expected))));
64
- lines.push(yamlLine(undefined, "actual", yamlString(RenderRescript$ReventlessGwt.renderMany(m.actual))));
65
- break;
66
- case "Throw" :
67
- lines.push(yamlLine(undefined, "error", yamlString(m.error)));
68
- lines.push(yamlLine(undefined, "stack", yamlString(m.stack)));
69
- break;
70
- }
29
+ let n = MismatchRender$ReventlessGwt.normalize(m);
30
+ let lines = [yamlLine(undefined, "kind", yamlString(n.kind))];
31
+ n.fields.forEach(f => {
32
+ if (typeof f !== "object") {
33
+ return;
34
+ }
35
+ switch (f.TAG) {
36
+ case "Expected" :
37
+ lines.push(yamlLine(undefined, "expected", yamlString(f._0)));
38
+ return;
39
+ case "Actual" :
40
+ lines.push(yamlLine(undefined, "actual", yamlString(f._0)));
41
+ return;
42
+ case "Key" :
43
+ lines.push(yamlLine(undefined, "key", yamlString(f._0)));
44
+ return;
45
+ case "Error" :
46
+ lines.push(yamlLine(undefined, "error", yamlString(f._0)));
47
+ return;
48
+ case "Stack" :
49
+ lines.push(yamlLine(undefined, "stack", yamlString(f._0)));
50
+ return;
51
+ }
52
+ });
71
53
  return lines.join("\n");
72
54
  }
73
55
 
@@ -45,12 +45,6 @@ let relativeToCwd = (abs: string) => pathRelative(processCwd(), abs)
45
45
 
46
46
  let sourceLineCache: Dict.t<option<array<string>>> = Dict.make()
47
47
 
48
- let resetLocateCache = () => {
49
- sourceLineCache
50
- ->Dict.keysToArray
51
- ->Array.forEach(k => Dict.delete(sourceLineCache, k))
52
- }
53
-
54
48
  let mjsToRes = (mjsPath: string): option<string> =>
55
49
  if String.endsWith(mjsPath, ".res.mjs") {
56
50
  Some(String.slice(mjsPath, ~start=0, ~end=String.length(mjsPath) - 4))
@@ -253,18 +247,9 @@ let deadCode = (findings: array<DomainDeadCode.finding>) =>
253
247
 
254
248
  // Event-Modeling graph (Phase 6) — the node/edge model assembled from each plugin's
255
249
  // `pluginStructure` + cross-plugin edges.
256
- let graph = (g: DomainGraph.graph) =>
257
- emit(
258
- Graph({
259
- nodes: g.nodes->Array.map((n): P.graphNode => {
260
- id: n.id,
261
- kind: n.kind,
262
- label: n.label,
263
- plugin: n.plugin,
264
- }),
265
- edges: g.edges->Array.map((e): P.graphEdge => {from: e.from, to_: e.to, kind: e.kind, label: ?e.label}),
266
- }),
267
- )
250
+ // `DomainGraph` already builds `P.graphNode`/`P.graphEdge`, so the graph event
251
+ // forwards them directly — no field-by-field re-map to drift from the contract.
252
+ let graph = (g: DomainGraph.graph) => emit(Graph({nodes: g.nodes, edges: g.edges}))
268
253
 
269
254
  // Component definitions (Phase 6.3) — one `encodePluginStructureEntry` JSON object
270
255
  // per plugin (commands/events with field schemas, read-side state schemas), used by
@@ -292,7 +277,7 @@ let domainEvent = (payload: JSON.t) => writeLine(JSON.stringify(payload))
292
277
 
293
278
  let platformLog = (~line: string) => emit(PlatformLog({line: line}))
294
279
 
295
- let platformStop = (~code: option<int>) => emit(PlatformStop({code: code}))
280
+ let platformStop = (~code: option<int>) => emit(PlatformStop({code: ?code}))
296
281
 
297
282
  let runStart = (~total: int, ~filter: array<string>) => emit(RunStart({total, filter}))
298
283
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  import * as Nodefs from "node:fs";
4
4
  import * as Nodepath from "node:path";
5
- import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
6
5
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
6
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
8
7
  import * as Hint$ReventlessGwt from "./Hint.res.mjs";
@@ -43,10 +42,6 @@ function relativeToCwd(abs) {
43
42
 
44
43
  let sourceLineCache = {};
45
44
 
46
- function resetLocateCache() {
47
- Object.keys(sourceLineCache).forEach(k => Stdlib_Dict.$$delete(sourceLineCache, k));
48
- }
49
-
50
45
  function mjsToRes(mjsPath) {
51
46
  if (mjsPath.endsWith(".res.mjs")) {
52
47
  return mjsPath.slice(0, mjsPath.length - 4 | 0);
@@ -282,24 +277,11 @@ function deadCode(findings) {
282
277
  }
283
278
 
284
279
  function graph(g) {
285
- let e_0 = g.nodes.map(n => ({
286
- id: n.id,
287
- kind: n.kind,
288
- label: n.label,
289
- plugin: n.plugin
290
- }));
291
- let e_1 = g.edges.map(e => ({
292
- from: e.from,
293
- to: e.to,
294
- kind: e.kind,
295
- label: e.label
296
- }));
297
- let e = {
280
+ let s = Protocol$ReventlessVscodeProtocol.toJsonLine({
298
281
  event: "graph",
299
- nodes: e_0,
300
- edges: e_1
301
- };
302
- let s = Protocol$ReventlessVscodeProtocol.toJsonLine(e);
282
+ nodes: g.nodes,
283
+ edges: g.edges
284
+ });
303
285
  process.stdout.write(s + "\n");
304
286
  }
305
287
 
@@ -527,7 +509,6 @@ export {
527
509
  fileLabelOf,
528
510
  relativeToCwd,
529
511
  sourceLineCache,
530
- resetLocateCache,
531
512
  mjsToRes,
532
513
  readLinesCached,
533
514
  escapeForDouble,
package/src/LocalHost.res CHANGED
@@ -55,23 +55,11 @@ type pluginExports = {"Make": platform => builtPlugin}
55
55
  // plugin keys match the names the framework itself bakes into Plugin.res. ──
56
56
 
57
57
  // "@scope/my-catalog" → "MyCatalog", "online-shop" → "OnlineShop".
58
- let packageNameToPluginName = (packageName: string): string => {
59
- let base = switch packageName->String.split("/") {
60
- | [_scope, local] => local
61
- | [local] => local
62
- | parts => parts->Array.getUnsafe(parts->Array.length - 1)
63
- }
64
- base
65
- ->String.split("-")
66
- ->Array.flatMap(part => part->String.split("_"))
67
- ->Array.map(word =>
68
- word === ""
69
- ? ""
70
- : word->String.slice(~start=0, ~end=1)->String.toUpperCase ++
71
- word->String.slice(~start=1, ~end=word->String.length)
72
- )
73
- ->Array.join("")
74
- }
58
+ // Single-sourced with the plugin generator (spec's `Config`) via
59
+ // `Reventless.PluginName` so the two can't drift — a drift silently breaks
60
+ // graph plugin keys (see the module for the full rationale). Re-exported here
61
+ // under the historical name the tests already pin.
62
+ let packageNameToPluginName = Reventless.PluginName.fromPackageName
75
63
 
76
64
  let strField = (json, key) =>
77
65
  json->JSON.Decode.object->Option.flatMap(d => d->Dict.get(key))->Option.flatMap(JSON.Decode.string)
@@ -82,19 +70,15 @@ let readJson = path =>
82
70
  }
83
71
 
84
72
  // plugin.json `name` → else PascalCase(package.json `name`) → else "Plugin".
73
+ // The precedence itself lives in `Reventless.PluginName.resolve`; this only
74
+ // reads the two raw fields with the local node bindings.
85
75
  let derivePluginName = (~pluginSrcDir: string): string => {
86
76
  let pluginJson = join(pluginSrcDir, "plugin.json")
87
- let fromPluginJson =
77
+ let pluginJsonName =
88
78
  existsSync(pluginJson) ? readJson(pluginJson)->Option.flatMap(j => strField(j, "name")) : None
89
- switch fromPluginJson {
90
- | Some(name) => name
91
- | None =>
92
- let pkgJson = join(dirname(pluginSrcDir), "package.json")
93
- readJson(pkgJson)
94
- ->Option.flatMap(j => strField(j, "name"))
95
- ->Option.map(packageNameToPluginName)
96
- ->Option.getOr("Plugin")
97
- }
79
+ let packageJsonName =
80
+ readJson(join(dirname(pluginSrcDir), "package.json"))->Option.flatMap(j => strField(j, "name"))
81
+ Reventless.PluginName.resolve(~pluginJsonName, ~packageJsonName)
98
82
  }
99
83
 
100
84
  // Given workspace package dirs (e.g. from PackageScan), keep those that carry a
@@ -7,6 +7,7 @@ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
7
7
  import * as Nodemodule from "node:module";
8
8
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
9
9
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
10
+ import * as PluginName$Reventless from "@reventlessdev/reventless-spec/src/components/PluginName.res.mjs";
10
11
 
11
12
  let dynamicImport = ((u) => import(u));
12
13
 
@@ -19,34 +20,6 @@ function bustedUrl(absolutePath) {
19
20
  return Nodeurl.pathToFileURL(absolutePath).href + "?t=" + counter.contents.toString();
20
21
  }
21
22
 
22
- function packageNameToPluginName(packageName) {
23
- let parts = packageName.split("/");
24
- let len = parts.length;
25
- let base;
26
- if (len >= 3) {
27
- base = parts[parts.length - 1 | 0];
28
- } else {
29
- switch (len) {
30
- case 0 :
31
- base = parts[parts.length - 1 | 0];
32
- break;
33
- case 1 :
34
- base = parts[0];
35
- break;
36
- case 2 :
37
- base = parts[1];
38
- break;
39
- }
40
- }
41
- return base.split("-").flatMap(part => part.split("_")).map(word => {
42
- if (word === "") {
43
- return "";
44
- } else {
45
- return word.slice(0, 1).toUpperCase() + word.slice(1, word.length);
46
- }
47
- }).join("");
48
- }
49
-
50
23
  function strField(json, key) {
51
24
  return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d[key]), Stdlib_JSON.Decode.string);
52
25
  }
@@ -61,12 +34,9 @@ function readJson(path) {
61
34
 
62
35
  function derivePluginName(pluginSrcDir) {
63
36
  let pluginJson = Nodepath.join(pluginSrcDir, "plugin.json");
64
- let fromPluginJson = Nodefs.existsSync(pluginJson) ? Stdlib_Option.flatMap(readJson(pluginJson), j => strField(j, "name")) : undefined;
65
- if (fromPluginJson !== undefined) {
66
- return fromPluginJson;
67
- }
68
- let pkgJson = Nodepath.join(Nodepath.dirname(pluginSrcDir), "package.json");
69
- return Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(readJson(pkgJson), j => strField(j, "name")), packageNameToPluginName), "Plugin");
37
+ let pluginJsonName = Nodefs.existsSync(pluginJson) ? Stdlib_Option.flatMap(readJson(pluginJson), j => strField(j, "name")) : undefined;
38
+ let packageJsonName = Stdlib_Option.flatMap(readJson(Nodepath.join(Nodepath.dirname(pluginSrcDir), "package.json")), j => strField(j, "name"));
39
+ return PluginName$Reventless.resolve(pluginJsonName, packageJsonName);
70
40
  }
71
41
 
72
42
  function discover(packageDirs) {
@@ -111,6 +81,8 @@ async function loadGraph(platformModulePath, plugins) {
111
81
  };
112
82
  }
113
83
 
84
+ let packageNameToPluginName = PluginName$Reventless.fromPackageName;
85
+
114
86
  export {
115
87
  dynamicImport,
116
88
  counter,
@@ -0,0 +1,72 @@
1
+ // Single source for rendering an `Outcome.mismatch`'s expected/actual/extras
2
+ // into RenderRescript strings. The Human and TAP formatters previously each held
3
+ // a full 10-arm switch over the mismatch that computed *identical* rendered
4
+ // values (`RenderRescript.render*`, the `Error()`/`Ok()` wrapping, the Todo
5
+ // `(id, value)` pairs) and differed only in framing — aligned `label: value`
6
+ // lines vs YAML `key: "escaped"` lines. This normalizes the value-rendering once;
7
+ // each formatter interprets the `field` list with its own framing.
8
+ //
9
+ // NOT used by JUnit (renders via the older JSON-based `Outcome.format`) nor
10
+ // FormatterJson/VsCode (they consume the raw JSON values to build a structured
11
+ // `{type, payload, rendered}` + `fieldDiff` payload).
12
+
13
+ // One rendered piece of a mismatch. The constructor carries the *already
14
+ // rendered* string (unescaped — Human uses it raw, TAP yaml-escapes it), except
15
+ // the structural markers `ExpectedNoEvents` (Human prints a literal line; TAP
16
+ // omits it) and `Stack` (Human appends it raw; TAP labels it as a yaml field).
17
+ type field =
18
+ | Expected(string)
19
+ | Actual(string)
20
+ | Key(string)
21
+ | ExpectedNoEvents
22
+ | Error(string)
23
+ | Stack(string)
24
+
25
+ type normalized = {
26
+ kind: string,
27
+ fields: array<field>,
28
+ }
29
+
30
+ let render = RenderRescript.render
31
+ let renderMany = RenderRescript.renderMany
32
+ let renderOption = RenderRescript.renderOption
33
+
34
+ let normalize = (m: Outcome.mismatch): normalized => {
35
+ let fields = switch m {
36
+ | EventsMismatch({expected, actual}) => [Expected(renderMany(expected)), Actual(renderMany(actual))]
37
+ | ErrorMismatch({expected, actual, actualEvents}) => [
38
+ Expected(`Error(${render(expected)})`),
39
+ Actual(
40
+ switch actual {
41
+ | Some(v) => `Error(${render(v)})`
42
+ | None => `Ok(${renderMany(actualEvents)})`
43
+ },
44
+ ),
45
+ ]
46
+ | StateMismatch({key, expected, actual}) => [
47
+ Key(key),
48
+ Expected(renderOption(expected)),
49
+ Actual(renderOption(actual)),
50
+ ]
51
+ | NoEventExpected({actual}) => [ExpectedNoEvents, Actual(renderMany(actual))]
52
+ | TodoMismatch({expected, actual}) =>
53
+ let fmt = (arr: array<(string, JSON.t)>) =>
54
+ "[" ++ arr->Array.map(((id, v)) => `(${id}, ${render(v)})`)->Array.join(", ") ++ "]"
55
+ [Expected(fmt(expected)), Actual(fmt(actual))]
56
+ | AppendConditionMismatch({expected, actual}) => [
57
+ Expected(render(expected)),
58
+ Actual(render(actual)),
59
+ ]
60
+ | TranslateError({expected, actual}) => [
61
+ Expected(expected),
62
+ Actual(actual->Option.getOr("(none)")),
63
+ ]
64
+ | QueryRowsMismatch({expected, actual}) => [Expected(renderMany(expected)), Actual(renderMany(actual))]
65
+ | PublishedActionsMismatch({expected, actual}) => [
66
+ Expected(renderMany(expected)),
67
+ Actual(renderMany(actual)),
68
+ ]
69
+ | Throw({error, stack}) => [Error(error), Stack(stack)]
70
+ }
71
+ {kind: Outcome.kindName(m), fields}
72
+ }
@@ -0,0 +1,127 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+ import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
5
+ import * as RenderRescript$ReventlessGwt from "./RenderRescript.res.mjs";
6
+
7
+ function normalize(m) {
8
+ let fields;
9
+ switch (m.TAG) {
10
+ case "ErrorMismatch" :
11
+ let actual = m.actual;
12
+ fields = [
13
+ {
14
+ TAG: "Expected",
15
+ _0: `Error(` + RenderRescript$ReventlessGwt.render(undefined, m.expected) + `)`
16
+ },
17
+ {
18
+ TAG: "Actual",
19
+ _0: actual !== undefined ? `Error(` + RenderRescript$ReventlessGwt.render(undefined, actual) + `)` : `Ok(` + RenderRescript$ReventlessGwt.renderMany(m.actualEvents) + `)`
20
+ }
21
+ ];
22
+ break;
23
+ case "StateMismatch" :
24
+ fields = [
25
+ {
26
+ TAG: "Key",
27
+ _0: m.key
28
+ },
29
+ {
30
+ TAG: "Expected",
31
+ _0: RenderRescript$ReventlessGwt.renderOption(m.expected)
32
+ },
33
+ {
34
+ TAG: "Actual",
35
+ _0: RenderRescript$ReventlessGwt.renderOption(m.actual)
36
+ }
37
+ ];
38
+ break;
39
+ case "NoEventExpected" :
40
+ fields = [
41
+ "ExpectedNoEvents",
42
+ {
43
+ TAG: "Actual",
44
+ _0: RenderRescript$ReventlessGwt.renderMany(m.actual)
45
+ }
46
+ ];
47
+ break;
48
+ case "TodoMismatch" :
49
+ let fmt = arr => "[" + arr.map(param => `(` + param[0] + `, ` + RenderRescript$ReventlessGwt.render(undefined, param[1]) + `)`).join(", ") + "]";
50
+ fields = [
51
+ {
52
+ TAG: "Expected",
53
+ _0: fmt(m.expected)
54
+ },
55
+ {
56
+ TAG: "Actual",
57
+ _0: fmt(m.actual)
58
+ }
59
+ ];
60
+ break;
61
+ case "AppendConditionMismatch" :
62
+ fields = [
63
+ {
64
+ TAG: "Expected",
65
+ _0: RenderRescript$ReventlessGwt.render(undefined, m.expected)
66
+ },
67
+ {
68
+ TAG: "Actual",
69
+ _0: RenderRescript$ReventlessGwt.render(undefined, m.actual)
70
+ }
71
+ ];
72
+ break;
73
+ case "TranslateError" :
74
+ fields = [
75
+ {
76
+ TAG: "Expected",
77
+ _0: m.expected
78
+ },
79
+ {
80
+ TAG: "Actual",
81
+ _0: Stdlib_Option.getOr(m.actual, "(none)")
82
+ }
83
+ ];
84
+ break;
85
+ case "Throw" :
86
+ fields = [
87
+ {
88
+ TAG: "Error",
89
+ _0: m.error
90
+ },
91
+ {
92
+ TAG: "Stack",
93
+ _0: m.stack
94
+ }
95
+ ];
96
+ break;
97
+ default:
98
+ fields = [
99
+ {
100
+ TAG: "Expected",
101
+ _0: RenderRescript$ReventlessGwt.renderMany(m.expected)
102
+ },
103
+ {
104
+ TAG: "Actual",
105
+ _0: RenderRescript$ReventlessGwt.renderMany(m.actual)
106
+ }
107
+ ];
108
+ }
109
+ return {
110
+ kind: Outcome$ReventlessGwt.kindName(m),
111
+ fields: fields
112
+ };
113
+ }
114
+
115
+ let render = RenderRescript$ReventlessGwt.render;
116
+
117
+ let renderMany = RenderRescript$ReventlessGwt.renderMany;
118
+
119
+ let renderOption = RenderRescript$ReventlessGwt.renderOption;
120
+
121
+ export {
122
+ render,
123
+ renderMany,
124
+ renderOption,
125
+ normalize,
126
+ }
127
+ /* No side effect */
package/src/Outcome.res CHANGED
@@ -95,67 +95,3 @@ let format = (m: mismatch) =>
95
95
  )}`
96
96
  | Throw({error, stack}) => `Throw: ${error}\n${stack}`
97
97
  }
98
-
99
- // Structured JSON payload per §3.3 of the analysis.
100
- // Closed schema — consumers switch on the `kind` discriminator.
101
- // Stage 7 enriches with `rendered` strings and `fieldDiff`.
102
- let toJson = (m: mismatch): JSON.t => {
103
- let obj = Dict.make()
104
- obj->Dict.set("kind", JSON.Encode.string(kindName(m)))
105
- let jsonArr = arr => JSON.Encode.array(arr)
106
- let optJson = o => o->Option.getOr(JSON.Encode.null)
107
- switch m {
108
- | EventsMismatch({expected, actual}) => {
109
- obj->Dict.set("expected", jsonArr(expected))
110
- obj->Dict.set("actual", jsonArr(actual))
111
- }
112
- | ErrorMismatch({expected, actual, actualEvents}) => {
113
- obj->Dict.set("expected", expected)
114
- obj->Dict.set("actual", optJson(actual))
115
- obj->Dict.set("actualEvents", jsonArr(actualEvents))
116
- }
117
- | StateMismatch({key, expected, actual}) => {
118
- obj->Dict.set("key", JSON.Encode.string(key))
119
- obj->Dict.set("expected", optJson(expected))
120
- obj->Dict.set("actual", optJson(actual))
121
- }
122
- | NoEventExpected({actual}) => obj->Dict.set("actual", jsonArr(actual))
123
- | TodoMismatch({expected, actual}) => {
124
- let pairArr = pairs =>
125
- pairs
126
- ->Array.map(((id, v)) => {
127
- let d = Dict.make()
128
- d->Dict.set("id", JSON.Encode.string(id))
129
- d->Dict.set("value", v)
130
- JSON.Encode.object(d)
131
- })
132
- ->jsonArr
133
- obj->Dict.set("expected", pairArr(expected))
134
- obj->Dict.set("actual", pairArr(actual))
135
- }
136
- | AppendConditionMismatch({expected, actual}) => {
137
- obj->Dict.set("expected", expected)
138
- obj->Dict.set("actual", actual)
139
- }
140
- | TranslateError({expected, actual}) => {
141
- obj->Dict.set("expected", JSON.Encode.string(expected))
142
- obj->Dict.set(
143
- "actual",
144
- actual->Option.mapOr(JSON.Encode.null, JSON.Encode.string),
145
- )
146
- }
147
- | QueryRowsMismatch({expected, actual}) => {
148
- obj->Dict.set("expected", jsonArr(expected))
149
- obj->Dict.set("actual", jsonArr(actual))
150
- }
151
- | PublishedActionsMismatch({expected, actual}) => {
152
- obj->Dict.set("expected", jsonArr(expected))
153
- obj->Dict.set("actual", jsonArr(actual))
154
- }
155
- | Throw({error, stack}) => {
156
- obj->Dict.set("error", JSON.Encode.string(error))
157
- obj->Dict.set("stack", JSON.Encode.string(stack))
158
- }
159
- }
160
- JSON.Encode.object(obj)
161
- }
@@ -76,52 +76,6 @@ function format(m) {
76
76
  }
77
77
  }
78
78
 
79
- function toJson(m) {
80
- let obj = {};
81
- obj["kind"] = kindName(m);
82
- switch (m.TAG) {
83
- case "ErrorMismatch" :
84
- obj["expected"] = m.expected;
85
- obj["actual"] = Stdlib_Option.getOr(m.actual, null);
86
- obj["actualEvents"] = m.actualEvents;
87
- break;
88
- case "StateMismatch" :
89
- obj["key"] = m.key;
90
- obj["expected"] = Stdlib_Option.getOr(m.expected, null);
91
- obj["actual"] = Stdlib_Option.getOr(m.actual, null);
92
- break;
93
- case "NoEventExpected" :
94
- obj["actual"] = m.actual;
95
- break;
96
- case "TodoMismatch" :
97
- let pairArr = pairs => pairs.map(param => {
98
- let d = {};
99
- d["id"] = param[0];
100
- d["value"] = param[1];
101
- return d;
102
- });
103
- obj["expected"] = pairArr(m.expected);
104
- obj["actual"] = pairArr(m.actual);
105
- break;
106
- case "TranslateError" :
107
- obj["expected"] = m.expected;
108
- obj["actual"] = Stdlib_Option.mapOr(m.actual, null, prim => prim);
109
- break;
110
- case "EventsMismatch" :
111
- case "AppendConditionMismatch" :
112
- case "QueryRowsMismatch" :
113
- case "PublishedActionsMismatch" :
114
- obj["expected"] = m.expected;
115
- obj["actual"] = m.actual;
116
- break;
117
- case "Throw" :
118
- obj["error"] = m.error;
119
- obj["stack"] = m.stack;
120
- break;
121
- }
122
- return obj;
123
- }
124
-
125
79
  let pass = {
126
80
  TAG: "Ok",
127
81
  _0: undefined
@@ -135,6 +89,5 @@ export {
135
89
  stringifyOptJson,
136
90
  stringifyJsonArray,
137
91
  format,
138
- toJson,
139
92
  }
140
93
  /* No side effect */
@@ -105,16 +105,20 @@ let inspectDir = (dir: string): option<platformPkg> => {
105
105
  // declaration order preserved). Prunes node_modules/lib/.git like Discovery.
106
106
  let rec walk = async (dir: string, acc: array<platformPkg>): array<platformPkg> => {
107
107
  switch inspectDir(dir) {
108
- | Some(pkg) => acc->Array.push(pkg) // a platform package — record, don't descend into its src
109
- | None => ()
110
- }
111
- let entries = try await _readdir(dir, {withFileTypes: true}) catch {
112
- | _ => []
113
- }
114
- for i in 0 to entries->Array.length - 1 {
115
- let entry = entries->Array.getUnsafe(i)
116
- if entry._isDirectory() && !Array.includes(ignoreNames, entry.name) {
117
- let _ = await walk(join(dir, entry.name), acc)
108
+ // A platform package — record it and stop: descending into its own subtree
109
+ // (src/, generated code, nested workspace copies) can only waste I/O or turn
110
+ // up spurious inner matches. This is what the "don't descend" comment always
111
+ // intended; the recursion below used to run regardless.
112
+ | Some(pkg) => acc->Array.push(pkg)
113
+ | None =>
114
+ let entries = try await _readdir(dir, {withFileTypes: true}) catch {
115
+ | _ => []
116
+ }
117
+ for i in 0 to entries->Array.length - 1 {
118
+ let entry = entries->Array.getUnsafe(i)
119
+ if entry._isDirectory() && !Array.includes(ignoreNames, entry.name) {
120
+ let _ = await walk(join(dir, entry.name), acc)
121
+ }
118
122
  }
119
123
  }
120
124
  acc