@reventlessdev/reventless-local 3.0.0-alpha.168 → 3.0.0-alpha.169

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.169 (2026-07-26)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **api:** preserve nullability for optional enum fields in generated GraphQL ([3110a4a](https://github.com/ReventlessDev/reventless-core/commit/3110a4af032440ba2639519f61f221960ac78fba))
11
+
12
+
6
13
  # 3.0.0-alpha.168 (2026-07-26)
7
14
 
8
15
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-local",
3
- "version": "3.0.0-alpha.168",
3
+ "version": "3.0.0-alpha.169",
4
4
  "description": "Local platform for Reventless (in-memory or SQLite backend, for development and testing without AWS)",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -33,15 +33,15 @@
33
33
  "ws": "^8.18.0",
34
34
  "@reventlessdev/rescript-effect": "0.1.0-alpha.31",
35
35
  "@reventlessdev/rescript-jest": "1.0.0-alpha.9",
36
+ "@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.25",
36
37
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
37
38
  "@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.20",
38
- "@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.25",
39
- "@reventlessdev/reventless-core": "3.0.0-alpha.181",
40
- "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.28",
41
- "@reventlessdev/reventless-gwt": "1.0.0-alpha.128",
42
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.45",
39
+ "@reventlessdev/reventless-core": "3.0.0-alpha.182",
40
+ "@reventlessdev/reventless-gwt": "1.0.0-alpha.129",
41
+ "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.29",
43
42
  "@reventlessdev/reventless-infra": "3.0.0-alpha.105",
44
- "@reventlessdev/reventless-spec": "3.0.0-alpha.81"
43
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.81",
44
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.46"
45
45
  },
46
46
  "devDependencies": {
47
47
  "rescript": "12.3.0",
@@ -27,6 +27,21 @@ type svState = {
27
27
  price: float,
28
28
  }
29
29
 
30
+ // Multi-variant enum used both as a required and as an optional state field, to pin
31
+ // the nullability the sury→GraphQL deriver must preserve for the optional case.
32
+ @schema
33
+ type sourceKind =
34
+ | @as("tagged") Tagged
35
+ | @as("ancestry") Ancestry
36
+ | @as("substrate") Substrate
37
+
38
+ @schema
39
+ type enumState = {
40
+ productId: @s.matches(Reventless.DcbTag.string) string,
41
+ requiredSource: sourceKind,
42
+ optionalSource?: sourceKind,
43
+ }
44
+
30
45
  @schema
31
46
  type addCommand = {
32
47
  productId: @s.matches(Reventless.DcbTag.string) string,
@@ -907,8 +922,38 @@ describe("Plugin admin fragment — kind enum + kindEq filter", () => {
907
922
  // kind is a real enum on the Plugin type (auto-derived from the payload-less variant)
908
923
  expect(sdl->String.includes("enum Platform_PluginKind"))->toBe(true)
909
924
  expect(sdl->String.includes("PlatformInfrastructure"))->toBe(true)
910
- expect(sdl->String.includes("kind: Platform_PluginKind!"))->toBe(true)
925
+ // `kind` is `option<pluginKind>` — nullable on purpose so a kind-less legacy row
926
+ // can't collapse the whole Platform_Plugins query. It must render without a `!`.
927
+ expect(sdl->String.includes("kind: Platform_PluginKind"))->toBe(true)
928
+ expect(sdl->String.includes("kind: Platform_PluginKind!"))->toBe(false)
911
929
  // @scan folds a server-side equality filter for the panel split
912
930
  expect(sdl->String.includes("kindEq: String"))->toBe(true)
913
931
  })
914
932
  })
933
+
934
+ describe("optional enum fields preserve GraphQL nullability", () => {
935
+ testPromise(
936
+ "required variant field → SomeEnum!; optional variant field → nullable SomeEnum",
937
+ async () => {
938
+ let fragment = ReventlessCore.GraphQL_FragmentGenerator.generate(
939
+ ~mutationEntries=[],
940
+ ~queryEntries=[
941
+ {
942
+ singleFieldName: "SV_Sourced",
943
+ listFieldName: "SV_Sourceds",
944
+ returnTypeName: "EnumState",
945
+ stateSchema: enumStateSchema->S.castToUnknown,
946
+ authorization: None,
947
+ includeIdParam: false,
948
+ },
949
+ ],
950
+ )
951
+ let sdl = ReventlessCore.GraphQL_SchemaInspector.inspectFragment(fragment).sdlPreview
952
+ // A required variant field stays non-null.
953
+ expect(sdl->String.includes("requiredSource: EnumStateRequiredSource!"))->toBe(true)
954
+ // An optional variant field is nullable: the enum is present but never with a `!`.
955
+ expect(sdl->String.includes("optionalSource: EnumStateOptionalSource"))->toBe(true)
956
+ expect(sdl->String.includes("optionalSource: EnumStateOptionalSource!"))->toBe(false)
957
+ },
958
+ )
959
+ })
@@ -29,6 +29,18 @@ let svStateSchema = S.schema(s => ({
29
29
  price: s.m(S.float)
30
30
  }));
31
31
 
32
+ let sourceKindSchema = S.union([
33
+ S.literal("tagged"),
34
+ S.literal("ancestry"),
35
+ S.literal("substrate")
36
+ ]);
37
+
38
+ let enumStateSchema = S.schema(s => ({
39
+ productId: s.m(DcbTag$Reventless.string),
40
+ requiredSource: s.m(sourceKindSchema),
41
+ optionalSource: s.m(S.option(sourceKindSchema))
42
+ }));
43
+
32
44
  let addCommandSchema = S.schema(s => ({
33
45
  productId: s.m(DcbTag$Reventless.string),
34
46
  name: s.m(S.string)
@@ -641,11 +653,29 @@ globalThis.describe("Plugin admin fragment — kind enum + kindEq filter", () =>
641
653
  let sdl = GraphQL_SchemaInspector$ReventlessCore.inspectFragment(fragment).sdlPreview;
642
654
  globalThis.expect(sdl.includes("enum Platform_PluginKind")).toBe(true);
643
655
  globalThis.expect(sdl.includes("PlatformInfrastructure")).toBe(true);
644
- globalThis.expect(sdl.includes("kind: Platform_PluginKind!")).toBe(true);
656
+ globalThis.expect(sdl.includes("kind: Platform_PluginKind")).toBe(true);
657
+ globalThis.expect(sdl.includes("kind: Platform_PluginKind!")).toBe(false);
645
658
  globalThis.expect(sdl.includes("kindEq: String")).toBe(true);
646
659
  });
647
660
  });
648
661
 
662
+ globalThis.describe("optional enum fields preserve GraphQL nullability", () => {
663
+ globalThis.test("required variant field → SomeEnum!; optional variant field → nullable SomeEnum", async () => {
664
+ let fragment = GraphQL_FragmentGenerator$ReventlessCore.generate([], [{
665
+ singleFieldName: "SV_Sourced",
666
+ listFieldName: "SV_Sourceds",
667
+ returnTypeName: "EnumState",
668
+ stateSchema: enumStateSchema,
669
+ authorization: undefined,
670
+ includeIdParam: false
671
+ }]);
672
+ let sdl = GraphQL_SchemaInspector$ReventlessCore.inspectFragment(fragment).sdlPreview;
673
+ globalThis.expect(sdl.includes("requiredSource: EnumStateRequiredSource!")).toBe(true);
674
+ globalThis.expect(sdl.includes("optionalSource: EnumStateOptionalSource")).toBe(true);
675
+ globalThis.expect(sdl.includes("optionalSource: EnumStateOptionalSource!")).toBe(false);
676
+ });
677
+ });
678
+
649
679
  let stringSchema = S.string;
650
680
 
651
681
  let taggedSchema = DcbTag$Reventless.string;
@@ -661,6 +691,8 @@ export {
661
691
  boolSchema,
662
692
  testStateSchema,
663
693
  svStateSchema,
694
+ sourceKindSchema,
695
+ enumStateSchema,
664
696
  addCommandSchema,
665
697
  unionCommandSchema,
666
698
  dcbSingleCommandSchema,