@reventlessdev/reventless-core 3.0.0-alpha.242 → 3.0.0-alpha.243

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 CHANGED
@@ -3,6 +3,13 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.243 (2026-08-20)
7
+
8
+ ### Features
9
+
10
+ * **spec:** type a geocoder's answer as one Geolocation value ([157be7a](https://github.com/ReventlessDev/reventless-core/commit/157be7aca4806ae19dba1f58979af13b88dc1821))
11
+
12
+
6
13
  # 3.0.0-alpha.242 (2026-08-20)
7
14
 
8
15
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.242",
3
+ "version": "3.0.0-alpha.243",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -30,15 +30,15 @@
30
30
  "uuid": "^13.0.0",
31
31
  "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
32
32
  "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.8",
33
+ "@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
33
34
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
34
35
  "@reventlessdev/rescript-node": "2.0.0-alpha.8",
35
- "@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
36
36
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
37
37
  "@reventlessdev/rescript-ssh2": "2.0.0-alpha.8",
38
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.148",
39
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.120",
38
40
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
39
- "@reventlessdev/reventless-interop": "3.0.0-alpha.32",
40
- "@reventlessdev/reventless-infra": "3.0.0-alpha.147",
41
- "@reventlessdev/reventless-spec": "3.0.0-alpha.119"
41
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.32"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "12.3.0",
@@ -0,0 +1,108 @@
1
+ open JestGlobals
2
+ open TaggedUnionFixtures
3
+
4
+ // Two markers ride on one schema; a lost one emits the field as `String` with
5
+ // nothing reporting it. Also pins a negative: the name does NOT come from
6
+ // `semanticCompositeNames`, so an entry there would be inert.
7
+
8
+ module GeoCustomersSpec = {
9
+ module Id = Reventless.Id.StringPure
10
+ let name = "SemanticGeolocationCustomers"
11
+ let moduleUrl: string = %raw(`import.meta.url`)
12
+
13
+ @schema
14
+ type state = {customerId: string, geolocation: Reventless.Geolocation.t}
15
+
16
+ let config = Reventless.ReadModel.config()
17
+ let subIdConfig = None
18
+ }
19
+
20
+ let fragment = GraphQL_FragmentGenerator.generate(
21
+ ~mutationEntries=[],
22
+ ~queryEntries=[
23
+ queryEntryFor(
24
+ ~returnTypeName="SemanticGeoCustomer",
25
+ ~specName=GeoCustomersSpec.name,
26
+ GeoCustomersSpec.stateSchema,
27
+ ),
28
+ ],
29
+ )
30
+
31
+ let types = GraphQL_Stitcher.decode(fragment).types
32
+ let typeDefFor = needle => types->Array.find(t => t->String.includes(needle))->Option.getOr("")
33
+
34
+ describe("the Geolocation semantic reaches the IR as a named union", () => {
35
+ let field = switch SchemaType.fromSuryObject(
36
+ ~typeName="SemanticGeoCustomer",
37
+ GeoCustomersSpec.stateSchema->S.castToUnknown,
38
+ ) {
39
+ | Some(fields) => fields->Dict.get("geolocation")
40
+ | None => None
41
+ }
42
+
43
+ // If the walk stopped at the marker, the field would classify as `Unknown`.
44
+ testSync("the semantic marker does not swallow the union", () => {
45
+ switch field {
46
+ | Some(Semantic({id}, TaggedUnion(name, arms))) =>
47
+ expect((id, name, arms->Array.length))->toEqual((
48
+ Reventless.Semantic.Id.geolocation,
49
+ "Geolocation",
50
+ 3,
51
+ ))
52
+ | _ => expect("geolocation")->toBe("Semantic(geolocation, TaggedUnion(Geolocation, 3 arms))")
53
+ }
54
+ })
55
+
56
+ // A path-composed name would carry the spec's name; this one must not.
57
+ testSync("the union is named from the schema, not from where it was found", () => {
58
+ switch field {
59
+ | Some(Semantic(_, TaggedUnion(name, _))) =>
60
+ expect(name->String.includes("Customer"))->toBe(false)
61
+ expect(name)->toBe("Geolocation")
62
+ | _ => expect("geolocation")->toBe("a named TaggedUnion")
63
+ }
64
+ })
65
+
66
+ // An entry there would leave this passing — it decides nothing.
67
+ testSync("semanticCompositeNames does not name this union", () => {
68
+ expect(SchemaType.canonicalName(Reventless.Semantic.Id.geolocation))->toEqual(None)
69
+ })
70
+ })
71
+
72
+ // The breaking half, asserted rather than assumed.
73
+ describe("a row in the pre-union shape does not decode", () => {
74
+ let oldShapedRow = JSON.parseOrThrow(`{
75
+ "customerId": "c1",
76
+ "location": {"lat": 48.2082, "lng": 16.3738},
77
+ "locationStatus": "Located",
78
+ "locationNote": null
79
+ }`)
80
+
81
+ testSync("the three-field shape is refused against the union schema", () => {
82
+ let decodes = switch oldShapedRow->S.parseOrThrow(~to=GeoCustomersSpec.stateSchema) {
83
+ | _ => true
84
+ | exception _ => false
85
+ }
86
+ expect(decodes)->toBe(false)
87
+ })
88
+ })
89
+
90
+ describe("the Geolocation semantic emits its three member types", () => {
91
+ testSync("the union declaration lists all three members", () => {
92
+ let union = typeDefFor("union Geolocation")
93
+ expect(union->String.includes("GeolocationPending"))->toBe(true)
94
+ expect(union->String.includes("GeolocationLocated"))->toBe(true)
95
+ expect(union->String.includes("GeolocationUnresolvable"))->toBe(true)
96
+ })
97
+
98
+ // Also says a composite nested in an arm keeps its own canonical name.
99
+ testSync("the located member carries a GeoPoint", () => {
100
+ expect(typeDefFor("type GeolocationLocated")->String.includes("GeoPoint"))->toBe(true)
101
+ })
102
+
103
+ testSync("the field is typed as the union", () => {
104
+ expect(typeDefFor("type SemanticGeoCustomer")->String.includes("geolocation: Geolocation"))->toBe(
105
+ true,
106
+ )
107
+ })
108
+ })
@@ -0,0 +1,122 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Sury from "sury";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Semantic$Reventless from "@reventlessdev/reventless-spec/src/semantic/Semantic.res.mjs";
7
+ import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
8
+ import * as Geolocation$Reventless from "@reventlessdev/reventless-spec/src/semantic/Geolocation.res.mjs";
9
+ import * as SchemaType$ReventlessCore from "../../src/components/Api/SchemaType.res.mjs";
10
+ import * as GraphQL_Stitcher$ReventlessCore from "../../src/components/Api/GraphQL_Stitcher.res.mjs";
11
+ import * as TaggedUnionFixtures$ReventlessCore from "../fixtures/TaggedUnionFixtures.res.mjs";
12
+ import * as GraphQL_FragmentGenerator$ReventlessCore from "../../src/components/Api/GraphQL_FragmentGenerator.res.mjs";
13
+
14
+ let name = "SemanticGeolocationCustomers";
15
+
16
+ let moduleUrl = import.meta.url;
17
+
18
+ let stateSchema = Sury.$schema(s => ({
19
+ customerId: s.m(Sury.string),
20
+ geolocation: s.m(Geolocation$Reventless.schema)
21
+ }));
22
+
23
+ let config = ReadModel$Reventless.config(undefined, undefined, undefined);
24
+
25
+ let GeoCustomersSpec = {
26
+ Id: undefined,
27
+ name: name,
28
+ moduleUrl: moduleUrl,
29
+ stateSchema: stateSchema,
30
+ config: config,
31
+ subIdConfig: undefined,
32
+ authorization: "AllowAuthenticated",
33
+ visibility: "Public"
34
+ };
35
+
36
+ let fragment = GraphQL_FragmentGenerator$ReventlessCore.generate([], [TaggedUnionFixtures$ReventlessCore.queryEntryFor("SemanticGeoCustomer", name, stateSchema)]);
37
+
38
+ let types = GraphQL_Stitcher$ReventlessCore.decode(fragment).types;
39
+
40
+ function typeDefFor(needle) {
41
+ return Stdlib_Option.getOr(types.find(t => t.includes(needle)), "");
42
+ }
43
+
44
+ globalThis.describe("the Geolocation semantic reaches the IR as a named union", () => {
45
+ let fields = SchemaType$ReventlessCore.fromSuryObject("SemanticGeoCustomer", stateSchema);
46
+ let field = fields !== undefined ? fields["geolocation"] : undefined;
47
+ globalThis.test("the semantic marker does not swallow the union", () => {
48
+ if (typeof field === "object" && field.TAG === "Semantic") {
49
+ let match = field._1;
50
+ if (typeof match === "object" && match.TAG === "TaggedUnion") {
51
+ globalThis.expect([
52
+ field._0.id,
53
+ match._0,
54
+ match._1.length
55
+ ]).toEqual([
56
+ Semantic$Reventless.Id.geolocation,
57
+ "Geolocation",
58
+ 3
59
+ ]);
60
+ return;
61
+ }
62
+ }
63
+ globalThis.expect("geolocation").toBe("Semantic(geolocation, TaggedUnion(Geolocation, 3 arms))");
64
+ });
65
+ globalThis.test("the union is named from the schema, not from where it was found", () => {
66
+ if (typeof field === "object" && field.TAG === "Semantic") {
67
+ let match = field._1;
68
+ if (typeof match === "object" && match.TAG === "TaggedUnion") {
69
+ let name = match._0;
70
+ globalThis.expect(name.includes("Customer")).toBe(false);
71
+ globalThis.expect(name).toBe("Geolocation");
72
+ return;
73
+ }
74
+ }
75
+ globalThis.expect("geolocation").toBe("a named TaggedUnion");
76
+ });
77
+ globalThis.test("semanticCompositeNames does not name this union", () => {
78
+ globalThis.expect(SchemaType$ReventlessCore.canonicalName(Semantic$Reventless.Id.geolocation)).toEqual(undefined);
79
+ });
80
+ });
81
+
82
+ globalThis.describe("a row in the pre-union shape does not decode", () => {
83
+ let oldShapedRow = JSON.parse(`{
84
+ "customerId": "c1",
85
+ "location": {"lat": 48.2082, "lng": 16.3738},
86
+ "locationStatus": "Located",
87
+ "locationNote": null
88
+ }`);
89
+ globalThis.test("the three-field shape is refused against the union schema", () => {
90
+ let decodes;
91
+ try {
92
+ S.parseOrThrow(oldShapedRow, stateSchema);
93
+ decodes = true;
94
+ } catch (exn) {
95
+ decodes = false;
96
+ }
97
+ globalThis.expect(decodes).toBe(false);
98
+ });
99
+ });
100
+
101
+ globalThis.describe("the Geolocation semantic emits its three member types", () => {
102
+ globalThis.test("the union declaration lists all three members", () => {
103
+ let union = typeDefFor("union Geolocation");
104
+ globalThis.expect(union.includes("GeolocationPending")).toBe(true);
105
+ globalThis.expect(union.includes("GeolocationLocated")).toBe(true);
106
+ globalThis.expect(union.includes("GeolocationUnresolvable")).toBe(true);
107
+ });
108
+ globalThis.test("the located member carries a GeoPoint", () => {
109
+ globalThis.expect(typeDefFor("type GeolocationLocated").includes("GeoPoint")).toBe(true);
110
+ });
111
+ globalThis.test("the field is typed as the union", () => {
112
+ globalThis.expect(typeDefFor("type SemanticGeoCustomer").includes("geolocation: Geolocation")).toBe(true);
113
+ });
114
+ });
115
+
116
+ export {
117
+ GeoCustomersSpec,
118
+ fragment,
119
+ types,
120
+ typeDefFor,
121
+ }
122
+ /* moduleUrl Not a pure module */