@reventlessdev/reventless-core 3.0.0-alpha.238 → 3.0.0-alpha.240

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 (178) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/package.json +12 -12
  3. package/src/Message.res +11 -11
  4. package/src/Message.res.mjs +15 -14
  5. package/src/Projection.res +2 -2
  6. package/src/Projection.res.mjs +5 -4
  7. package/src/adapter/Monitoring/Monitoring.res +24 -2
  8. package/src/adapter/Monitoring/Monitoring.res.mjs +3 -3
  9. package/src/admin/Platform_AdminApi.res.mjs +3 -3
  10. package/src/admin/Platform_Admin_Structure.res +33 -112
  11. package/src/admin/Platform_Admin_Structure.res.mjs +8 -138
  12. package/src/admin/Platform_ComponentDefinitionsApi.res +2 -1
  13. package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +5 -1
  14. package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res.mjs +12 -12
  15. package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments.res.mjs +14 -14
  16. package/src/components/Aggregate/Aggregate_Callback.res +2 -2
  17. package/src/components/Aggregate/Aggregate_Callback.res.mjs +4 -4
  18. package/src/components/Api/ApiAllowedStatesHelpers.res.mjs +3 -3
  19. package/src/components/Api/ApiNoApiHelpers.res.mjs +5 -5
  20. package/src/components/Api/ApiTargetStateHelpers.res.mjs +3 -3
  21. package/src/components/Api/GraphQL_FragmentGenerator.res +140 -24
  22. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +48 -21
  23. package/src/components/Api/GraphQL_SchemaInspector.res +1 -1
  24. package/src/components/Api/GraphQL_SchemaInspector.res.mjs +1 -1
  25. package/src/components/Api/MCP_SchemaGenerator.res +1 -1
  26. package/src/components/Api/MCP_SchemaGenerator.res.mjs +1 -1
  27. package/src/components/Api/SchemaType.res +6 -10
  28. package/src/components/Api/SchemaType.res.mjs +4 -4
  29. package/src/components/Api/StorageRefFields.res +1 -1
  30. package/src/components/Api/StorageRefFields.res.mjs +1 -1
  31. package/src/components/Api/SuryToJsonSchema.res +19 -0
  32. package/src/components/Api/SuryToJsonSchema.res.mjs +10 -5
  33. package/src/components/AutomationSlice/AutomationSlice_Callback.res +3 -4
  34. package/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs +22 -23
  35. package/src/components/CommandTopic/CommandTopic_Callback.res +1 -2
  36. package/src/components/CommandTopic/CommandTopic_Callback.res.mjs +3 -4
  37. package/src/components/CommandTopic/CommandTopic_Helpers.res +1 -1
  38. package/src/components/CommandTopic/CommandTopic_Helpers.res.mjs +3 -3
  39. package/src/components/Counter/Counter.res.mjs +2 -2
  40. package/src/components/Counter/Counter_Callback.res.mjs +4 -4
  41. package/src/components/Counter/Counter_Operations.res.mjs +4 -4
  42. package/src/components/Dcb/Dcb_Builder.res +1 -1
  43. package/src/components/Dcb/Dcb_Builder.res.mjs +8 -6
  44. package/src/components/DcbEventLog/DcbEventLog_Builder.res.mjs +4 -3
  45. package/src/components/EventCollector/EventCollector_Builder.res +1 -1
  46. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +1 -2
  47. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +11 -12
  48. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res +3 -4
  49. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs +24 -25
  50. package/src/components/SideEffectHandler/SideEffectHandler_Callback.res +1 -1
  51. package/src/components/SideEffectHandler/SideEffectHandler_Callback.res.mjs +3 -3
  52. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +2 -2
  53. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +4 -4
  54. package/src/components/StateViewSlice/StateViewSlice_Builder.res +1 -1
  55. package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +3 -3
  56. package/src/plugin/api/PluginBaseFragment.res +12 -29
  57. package/src/plugin/api/PluginBaseFragment.res.mjs +3 -22
  58. package/src/plugin/component/Capability_Inference.res +118 -1
  59. package/src/plugin/component/Capability_Inference.res.mjs +128 -1
  60. package/src/plugin/component/Plugin_Builder.res +9 -2
  61. package/src/plugin/component/Plugin_Builder.res.mjs +5 -2
  62. package/src/plugin/component/Plugin_Helpers.res +11 -11
  63. package/src/plugin/component/Plugin_Helpers.res.mjs +11 -10
  64. package/src/plugin/component/Plugin_Structure.res +458 -296
  65. package/src/plugin/component/Plugin_Structure.res.mjs +259 -182
  66. package/src/plugin/component/Plugin_SubscriptionSchema.res +1 -1
  67. package/src/plugin/component/Plugin_SubscriptionSchema.res.mjs +1 -1
  68. package/src/plugin/component/SchemaWalker.res +11 -11
  69. package/src/plugin/component/SchemaWalker.res.mjs +8 -9
  70. package/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs +15 -15
  71. package/src/plugin/lifecycle/PluginBehavior.res.mjs +55 -55
  72. package/src/plugin/lifecycle/PluginSpec.res +22 -7
  73. package/src/plugin/lifecycle/PluginSpec.res.mjs +91 -36
  74. package/src/plugin/lifecycle/PluginsReadModelSpec.res +36 -9
  75. package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +33 -22
  76. package/src/util/Interstack.res +3 -3
  77. package/src/util/LogFormat.res +1 -2
  78. package/src/util/LogFormat.res.mjs +2 -2
  79. package/tests/IdentityTest.res +6 -7
  80. package/tests/IdentityTest.res.mjs +7 -9
  81. package/tests/adapter/MonitoringTest.res +30 -3
  82. package/tests/adapter/MonitoringTest.res.mjs +31 -12
  83. package/tests/adapter/RuntimeExtensionHookReachTest.res +0 -1
  84. package/tests/adapter/RuntimeExtensionHookReachTest.res.mjs +5 -7
  85. package/tests/admin/Platform_Admin_StructureTest.res +254 -0
  86. package/tests/admin/Platform_Admin_StructureTest.res.mjs +219 -0
  87. package/tests/admin/Platform_BakedManifestTest.res +1 -0
  88. package/tests/admin/Platform_BakedManifestTest.res.mjs +1 -0
  89. package/tests/admin/Platform_ComponentDefinitionsApiTest.res +3 -0
  90. package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +6 -0
  91. package/tests/admin/Platform_PluginStructuresApiTest.res +1 -0
  92. package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +2 -0
  93. package/tests/aggregate/AggregateCacheTest.res +0 -1
  94. package/tests/aggregate/AggregateCacheTest.res.mjs +13 -15
  95. package/tests/aggregate/AggregateConflictTest.res +0 -1
  96. package/tests/aggregate/AggregateConflictTest.res.mjs +7 -9
  97. package/tests/aggregate/AggregateFixtures.res +0 -1
  98. package/tests/aggregate/AggregateFixtures.res.mjs +19 -21
  99. package/tests/aggregate/AggregateSnapshotTest.res +1 -2
  100. package/tests/aggregate/AggregateSnapshotTest.res.mjs +15 -16
  101. package/tests/api/ApiNoApiTest.res.mjs +17 -9
  102. package/tests/api/GraphQL_FragmentGeneratorTest.res +232 -0
  103. package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +207 -55
  104. package/tests/api/PsInternalFieldsReadModel.res +21 -0
  105. package/tests/api/PsInternalFieldsReadModel.res.mjs +74 -0
  106. package/tests/api/StorageRefFieldsTest.res.mjs +13 -13
  107. package/tests/api/SuryToJsonSchemaTest.res +73 -3
  108. package/tests/api/SuryToJsonSchemaTest.res.mjs +364 -816
  109. package/tests/commandgenerator/CommandGeneratorFixtures.res +0 -1
  110. package/tests/commandgenerator/CommandGeneratorFixtures.res.mjs +19 -21
  111. package/tests/commandgenerator/OwnerStampingTest.res +0 -1
  112. package/tests/commandgenerator/OwnerStampingTest.res.mjs +9 -11
  113. package/tests/commandtopic/CommandTopicCallbackFixtures.res +0 -1
  114. package/tests/commandtopic/CommandTopicCallbackFixtures.res.mjs +7 -9
  115. package/tests/counter/CounterFixtures.res +0 -1
  116. package/tests/counter/CounterFixtures.res.mjs +1 -4
  117. package/tests/dcb/DcbDecodeTest.res.mjs +24 -24
  118. package/tests/dcb/DcbEventLogOperationsTest.res +2 -2
  119. package/tests/dcb/DcbEventLogOperationsTest.res.mjs +3 -3
  120. package/tests/dcb/DcbEventLogSchemaTest.res.mjs +7 -7
  121. package/tests/dcb/DcbFixtures.res +0 -1
  122. package/tests/dcb/DcbFixtures.res.mjs +87 -89
  123. package/tests/dcb/DcbTagTest.res.mjs +6 -6
  124. package/tests/dcb/DcbValidationTest.res.mjs +52 -52
  125. package/tests/eventlog/EventLogFixtures.res +0 -1
  126. package/tests/eventlog/EventLogFixtures.res.mjs +7 -9
  127. package/tests/eventmapper/EventMapperFixtures.res +0 -1
  128. package/tests/eventmapper/EventMapperFixtures.res.mjs +24 -26
  129. package/tests/eventtopic/EventTopicFixtures.res +0 -1
  130. package/tests/eventtopic/EventTopicFixtures.res.mjs +7 -9
  131. package/tests/extensionpoint/ExtensionPointFixtures.res +0 -1
  132. package/tests/extensionpoint/ExtensionPointFixtures.res.mjs +10 -12
  133. package/tests/extensionpoint/ExtensionPointOperationsTest.res +1 -2
  134. package/tests/extensionpoint/ExtensionPointOperationsTest.res.mjs +9 -10
  135. package/tests/logger/LogFormatTest.res +3 -2
  136. package/tests/logger/LogFormatTest.res.mjs +7 -9
  137. package/tests/message/MessageTest.res +2 -2
  138. package/tests/message/MessageTest.res.mjs +8 -7
  139. package/tests/message/MetaEnvelopeTest.res +5 -5
  140. package/tests/message/MetaEnvelopeTest.res.mjs +12 -11
  141. package/tests/plugin/ManifestVisibilityTest.res.mjs +7 -7
  142. package/tests/plugin/PluginDefinitionScalars.res +5 -3
  143. package/tests/plugin/PluginDefinitionScalars.res.mjs +2 -2
  144. package/tests/plugin/PluginLifecycleCorpusTest.res +4 -3
  145. package/tests/plugin/PluginLifecycleCorpusTest.res.mjs +3 -6
  146. package/tests/plugin/PluginSpecExperiment.res +0 -1
  147. package/tests/plugin/PluginSpecExperiment.res.mjs +12 -14
  148. package/tests/plugin/PluginStructureTest.res +292 -2
  149. package/tests/plugin/PluginStructureTest.res.mjs +391 -88
  150. package/tests/plugin/Plugin_EventQuerySchemaTest.res.mjs +3 -3
  151. package/tests/plugin/SchemaWalkerTest.res +0 -1
  152. package/tests/plugin/SchemaWalkerTest.res.mjs +17 -19
  153. package/tests/plugin/StateChangeSlice/PsAttachInvoice.res.mjs +5 -5
  154. package/tests/plugin/StateChangeSlice/PsChangePhoto.res.mjs +8 -8
  155. package/tests/plugin/StateChangeSlice/PsDispatchShipment.res.mjs +5 -5
  156. package/tests/plugin/StateChangeSlice/PsGatedCommands.res.mjs +9 -9
  157. package/tests/plugin/StateChangeSlice/PsPlaceOrder.res.mjs +5 -5
  158. package/tests/plugin/StateChangeSlice/PsReserveStock.res.mjs +8 -8
  159. package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +15 -15
  160. package/tests/plugin/StateChangeSlice/PsTypoStore.res +31 -0
  161. package/tests/plugin/StateChangeSlice/PsTypoStore.res.mjs +62 -0
  162. package/tests/plugin/StateChangeSlice/PsUploadAvatar.res.mjs +8 -8
  163. package/tests/plugin/StateChangeSlice/PsUploadImages.res +40 -0
  164. package/tests/plugin/StateChangeSlice/PsUploadImages.res.mjs +66 -0
  165. package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +13 -12
  166. package/tests/plugin/StateViewSlice/PsAvailableProductsView.res.mjs +8 -8
  167. package/tests/plugin/StateViewSlice/PsCategoriesView.res.mjs +6 -6
  168. package/tests/plugin/StateViewSlice/PsCustomersView.res.mjs +10 -10
  169. package/tests/plugin/StateViewSlice/PsGatedView.res.mjs +6 -6
  170. package/tests/plugin/StateViewSlice/PsOrdersView.res.mjs +9 -9
  171. package/tests/plugin/StateViewSlice/PsShipmentsView.res.mjs +11 -10
  172. package/tests/plugin/pluginDefinitionRequiredScalars.txt +15 -0
  173. package/tests/querydb/QueryDbFixtures.res +0 -1
  174. package/tests/querydb/QueryDbFixtures.res.mjs +5 -7
  175. package/tests/sideeffecthandler/SideEffectHandlerFixtures.res +0 -1
  176. package/tests/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +4 -6
  177. package/tests/util/CommandPublisherTest.res +0 -1
  178. package/tests/util/CommandPublisherTest.res.mjs +4 -6
@@ -1,26 +1,24 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
3
+ import * as Sury from "sury";
4
4
  import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
5
5
  import * as Effect from "effect/Effect";
6
6
  import * as Stream from "effect/Stream";
7
7
  import * as Aggregate_Callback$ReventlessCore from "../../src/components/Aggregate/Aggregate_Callback.res.mjs";
8
8
 
9
- S.enableJson();
10
-
11
9
  let name = "ConflictTestAggregate";
12
10
 
13
- let commandSchema = S.schema(s => ({
11
+ let commandSchema = Sury.$schema(s => ({
14
12
  TAG: "Create",
15
- name: s.m(S.string)
13
+ name: s.m(Sury.string)
16
14
  }));
17
15
 
18
- let eventSchema = S.schema(s => ({
16
+ let eventSchema = Sury.$schema(s => ({
19
17
  TAG: "Created",
20
- name: s.m(S.string)
18
+ name: s.m(Sury.string)
21
19
  }));
22
20
 
23
- let errorSchema = S.literal("AlreadyExists");
21
+ let errorSchema = Sury.literal("AlreadyExists");
24
22
 
25
23
  let moduleUrl = import.meta.url;
26
24
 
@@ -383,4 +381,4 @@ export {
383
381
  testMeta,
384
382
  makeTopicItem,
385
383
  }
386
- /* Not a pure module */
384
+ /* commandSchema Not a pure module */
@@ -1,4 +1,3 @@
1
- S.enableJson()
2
1
 
3
2
  // ─────────────────────────────────────────────────────────────
4
3
  // Aggregate spec
@@ -1,39 +1,37 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
3
+ import * as Sury from "sury";
4
4
  import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
5
5
  import * as Stream from "effect/Stream";
6
6
  import * as Aggregate_Callback$ReventlessCore from "../../src/components/Aggregate/Aggregate_Callback.res.mjs";
7
7
 
8
- S.enableJson();
9
-
10
8
  let name = "TestAggregate";
11
9
 
12
- let commandSchema = S.union([
13
- S.schema(s => ({
10
+ let commandSchema = Sury.union([
11
+ Sury.$schema(s => ({
14
12
  TAG: "Create",
15
- name: s.m(S.string)
13
+ name: s.m(Sury.string)
16
14
  })),
17
- S.schema(s => ({
15
+ Sury.$schema(s => ({
18
16
  TAG: "Rename",
19
- newName: s.m(S.string)
17
+ newName: s.m(Sury.string)
20
18
  }))
21
19
  ]);
22
20
 
23
- let eventSchema = S.union([
24
- S.schema(s => ({
21
+ let eventSchema = Sury.union([
22
+ Sury.$schema(s => ({
25
23
  TAG: "Created",
26
- name: s.m(S.string)
24
+ name: s.m(Sury.string)
27
25
  })),
28
- S.schema(s => ({
26
+ Sury.$schema(s => ({
29
27
  TAG: "Renamed",
30
- newName: s.m(S.string)
28
+ newName: s.m(Sury.string)
31
29
  }))
32
30
  ]);
33
31
 
34
- let errorSchema = S.union([
35
- S.literal("AlreadyExists"),
36
- S.literal("NotFound")
32
+ let errorSchema = Sury.union([
33
+ Sury.literal("AlreadyExists"),
34
+ Sury.literal("NotFound")
37
35
  ]);
38
36
 
39
37
  let moduleUrl = import.meta.url;
@@ -52,11 +50,11 @@ let AggSpec = {
52
50
  commandAuthorization: commandAuthorization
53
51
  };
54
52
 
55
- let stateSchema = S.union([
56
- S.literal("NotCreated"),
57
- S.schema(s => ({
53
+ let stateSchema = Sury.union([
54
+ Sury.literal("NotCreated"),
55
+ Sury.$schema(s => ({
58
56
  TAG: "Created",
59
- name: s.m(S.string)
57
+ name: s.m(Sury.string)
60
58
  }))
61
59
  ]);
62
60
 
@@ -305,4 +303,4 @@ export {
305
303
  testMeta,
306
304
  makeTopicItem,
307
305
  }
308
- /* Not a pure module */
306
+ /* commandSchema Not a pure module */
@@ -12,7 +12,6 @@
12
12
 
13
13
  open JestGlobals
14
14
 
15
- S.enableJson()
16
15
 
17
16
  module AggSpec = {
18
17
  module Id = Reventless.Id.StringPure
@@ -300,7 +299,7 @@ describe("Aggregate_Callback — snapshots:", () => {
300
299
  | Some(snap) =>
301
300
  expect(snap.seqNr)->toBe(20)
302
301
  // 20 items folded through the seeded state — no double-count, no gap.
303
- let decoded = snap.state->S.parseJsonOrThrow(TestBehavior.stateSchema)
302
+ let decoded = snap.state->Reventless.Util_Sury.fromJson(TestBehavior.stateSchema)
304
303
  expect(decoded.items->Array.length)->toBe(20)
305
304
  | None => expect("expected snapshot at 20")->toBe("none")
306
305
  }
@@ -1,31 +1,30 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
3
+ import * as Sury from "sury";
4
4
  import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
5
5
  import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
6
6
  import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
7
7
  import * as Effect from "effect/Effect";
8
8
  import * as Stream$1 from "effect/Stream";
9
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
9
10
  import * as Aggregate_Callback$ReventlessCore from "../../src/components/Aggregate/Aggregate_Callback.res.mjs";
10
11
 
11
- S.enableJson();
12
-
13
12
  let name = "SnapshotTestAggregate";
14
13
 
15
- let commandSchema = S.union([
16
- S.schema(s => ({
14
+ let commandSchema = Sury.union([
15
+ Sury.$schema(s => ({
17
16
  TAG: "Add",
18
- item: s.m(S.string)
17
+ item: s.m(Sury.string)
19
18
  })),
20
- S.literal("Noop")
19
+ Sury.literal("Noop")
21
20
  ]);
22
21
 
23
- let eventSchema = S.schema(s => ({
22
+ let eventSchema = Sury.$schema(s => ({
24
23
  TAG: "Added",
25
- item: s.m(S.string)
24
+ item: s.m(Sury.string)
26
25
  }));
27
26
 
28
- let errorSchema = S.literal("Never");
27
+ let errorSchema = Sury.literal("Never");
29
28
 
30
29
  let moduleUrl = import.meta.url;
31
30
 
@@ -43,8 +42,8 @@ let AggSpec = {
43
42
  commandAuthorization: commandAuthorization
44
43
  };
45
44
 
46
- let stateSchema = S.schema(s => ({
47
- items: s.m(S.array(S.string))
45
+ let stateSchema = Sury.$schema(s => ({
46
+ items: s.m(Sury.array(Sury.string))
48
47
  }));
49
48
 
50
49
  let initialState = {
@@ -325,8 +324,8 @@ let TestHandler = Aggregate_Callback$ReventlessCore.Make({
325
324
  eventLog: eventLog
326
325
  });
327
326
 
328
- let stateSchema$1 = S.schema(s => ({
329
- items: s.m(S.array(S.string))
327
+ let stateSchema$1 = Sury.$schema(s => ({
328
+ items: s.m(Sury.array(Sury.string))
330
329
  }));
331
330
 
332
331
  let initialState$1 = {
@@ -550,7 +549,7 @@ globalThis.describe("Aggregate_Callback — snapshots:", () => {
550
549
  let snap = mock.getSnapshot("agg-1");
551
550
  if (snap !== undefined) {
552
551
  globalThis.expect(snap.seqNr).toBe(20);
553
- let decoded = S.parseJsonOrThrow(snap.state, stateSchema);
552
+ let decoded = Util_Sury$Reventless.fromJson(snap.state, stateSchema);
554
553
  globalThis.expect(decoded.items.length).toBe(20);
555
554
  } else {
556
555
  globalThis.expect("expected snapshot at 20").toBe("none");
@@ -642,4 +641,4 @@ export {
642
641
  run,
643
642
  tick,
644
643
  }
645
- /* Not a pure module */
644
+ /* commandSchema Not a pure module */
@@ -1,37 +1,42 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
3
+ import * as Sury from "sury";
4
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
5
  import * as Api$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Api.res.mjs";
6
6
  import * as ApiNoApiHelpers$ReventlessCore from "../../src/components/Api/ApiNoApiHelpers.res.mjs";
7
7
 
8
8
  globalThis.describe("ApiNoApiHelpers:", () => {
9
9
  globalThis.test("isNoApi returns false for schema without noApi metadata", () => {
10
- globalThis.expect(ApiNoApiHelpers$ReventlessCore.isNoApi(S.string)).toBe(false);
10
+ let schema = Sury.string;
11
+ globalThis.expect(ApiNoApiHelpers$ReventlessCore.isNoApi(schema)).toBe(false);
11
12
  });
12
13
  globalThis.test("isNoApi returns true for schema with noApi metadata set to true", () => {
13
- let schema$p = S.Metadata.set(S.string, Api$ReventlessInfra.noApiId, true);
14
+ let schema = Sury.string;
15
+ let schema$p = Sury.$Metadata_set(schema, Api$ReventlessInfra.noApiId, true);
14
16
  globalThis.expect(ApiNoApiHelpers$ReventlessCore.isNoApi(schema$p)).toBe(true);
15
17
  });
16
18
  globalThis.test("getExcludedVariants returns None for schema without noApiVariants metadata", () => {
17
- globalThis.expect(ApiNoApiHelpers$ReventlessCore.getExcludedVariants(S.string)).toBe(undefined);
19
+ let schema = Sury.string;
20
+ globalThis.expect(ApiNoApiHelpers$ReventlessCore.getExcludedVariants(schema)).toBe(undefined);
18
21
  });
19
22
  globalThis.test("getExcludedVariants returns Some with DeleteItem when set", () => {
23
+ let schema = Sury.string;
20
24
  let excluded = new Set([
21
25
  "DeleteItem",
22
26
  "UpdateItem"
23
27
  ]);
24
- let schema$p = S.Metadata.set(S.string, Api$ReventlessInfra.noApiVariantsId, excluded);
28
+ let schema$p = Sury.$Metadata_set(schema, Api$ReventlessInfra.noApiVariantsId, excluded);
25
29
  let result = ApiNoApiHelpers$ReventlessCore.getExcludedVariants(schema$p);
26
30
  globalThis.expect(Stdlib_Option.getOr(Stdlib_Option.map(result, set => set.has("DeleteItem")), false)).toBe(true);
27
31
  });
28
32
  globalThis.test("filterNoApiVariants returns all field names when no variants are excluded", () => {
33
+ let schema = Sury.string;
29
34
  let fieldNames = [
30
35
  "CreateItem",
31
36
  "DeleteItem",
32
37
  "UpdateItem"
33
38
  ];
34
- let filtered = ApiNoApiHelpers$ReventlessCore.filterNoApiVariants(fieldNames, S.string);
39
+ let filtered = ApiNoApiHelpers$ReventlessCore.filterNoApiVariants(fieldNames, schema);
35
40
  globalThis.expect(filtered).toEqual([
36
41
  "CreateItem",
37
42
  "DeleteItem",
@@ -39,8 +44,9 @@ globalThis.describe("ApiNoApiHelpers:", () => {
39
44
  ]);
40
45
  });
41
46
  globalThis.test("filterNoApiVariants filters out excluded variants", () => {
47
+ let schema = Sury.string;
42
48
  let excluded = new Set(["DeleteItem"]);
43
- let schema$p = S.Metadata.set(S.string, Api$ReventlessInfra.noApiVariantsId, excluded);
49
+ let schema$p = Sury.$Metadata_set(schema, Api$ReventlessInfra.noApiVariantsId, excluded);
44
50
  let fieldNames = [
45
51
  "CreateItem",
46
52
  "DeleteItem",
@@ -53,11 +59,12 @@ globalThis.describe("ApiNoApiHelpers:", () => {
53
59
  ]);
54
60
  });
55
61
  globalThis.test("filterNoApiVariants filters out multiple excluded variants", () => {
62
+ let schema = Sury.string;
56
63
  let excluded = new Set([
57
64
  "DeleteItem",
58
65
  "UpdateItem"
59
66
  ]);
60
- let schema$p = S.Metadata.set(S.string, Api$ReventlessInfra.noApiVariantsId, excluded);
67
+ let schema$p = Sury.$Metadata_set(schema, Api$ReventlessInfra.noApiVariantsId, excluded);
61
68
  let fieldNames = [
62
69
  "CreateItem",
63
70
  "DeleteItem",
@@ -71,12 +78,13 @@ globalThis.describe("ApiNoApiHelpers:", () => {
71
78
  ]);
72
79
  });
73
80
  globalThis.test("filterNoApiVariants returns empty array when all variants are excluded", () => {
81
+ let schema = Sury.string;
74
82
  let excluded = new Set([
75
83
  "CreateItem",
76
84
  "DeleteItem",
77
85
  "UpdateItem"
78
86
  ]);
79
- let schema$p = S.Metadata.set(S.string, Api$ReventlessInfra.noApiVariantsId, excluded);
87
+ let schema$p = Sury.$Metadata_set(schema, Api$ReventlessInfra.noApiVariantsId, excluded);
80
88
  let fieldNames = [
81
89
  "CreateItem",
82
90
  "DeleteItem",
@@ -340,3 +340,235 @@ describe("GraphQL_FragmentGenerator.mutationArgTypes", () => {
340
340
  expect((mismatched, field->String.length > 0))->toEqual(([], true))
341
341
  })
342
342
  })
343
+
344
+ module RefDoorRow = {
345
+ @schema
346
+ type state = {id: string, name: string}
347
+ }
348
+
349
+ let queryFor = (fragment, needle) =>
350
+ GraphQL_Stitcher.decode(fragment).queries->Array.find(q => q->String.includes(needle))
351
+
352
+ let refDoorFragment = (~subIdField=?) =>
353
+ GraphQL_FragmentGenerator.generate(
354
+ ~mutationEntries=[],
355
+ ~queryEntries=[
356
+ {
357
+ ReventlessInfra.Api.singleFieldName: "Catalog_Product",
358
+ listFieldName: "Catalog_Products",
359
+ returnTypeName: "Catalog_Product",
360
+ stateSchema: RefDoorRow.stateSchema->S.castToUnknown,
361
+ authorization: None,
362
+ connectionSpec: true,
363
+ subIdField: ?subIdField,
364
+ },
365
+ ],
366
+ )
367
+
368
+ // What a caller holding a pointer to a withheld row may learn about it, and what
369
+ // the two doors that could never answer for one can now be asked.
370
+ describe("the reference door and the archive argument", () => {
371
+
372
+ // The narrowness is the type's, not a rule each backend re-implements: a caller
373
+ // cannot ask this door for a price, because there is no price on it.
374
+ testSync("projects a reference to id, label and the state that retired it", () => {
375
+ let sdl = GraphQL_FragmentGenerator.deriveRefTypeSdl(~returnTypeName="Catalog_Product")
376
+ expect((
377
+ sdl->String.includes("type Catalog_ProductRef"),
378
+ sdl->String.includes("label: String!"),
379
+ sdl->String.includes("retiredState: String"),
380
+ sdl->String.includes("price"),
381
+ ))->toEqual((true, true, true, false))
382
+ })
383
+
384
+ // Emitted for every view rather than only the annotated ones, on the reasoning
385
+ // `includeRetired` already states: a field that appears with an annotation makes
386
+ // adding or removing it a breaking schema change.
387
+ testSync("names the door after the list field it resolves against", () =>
388
+ expect(
389
+ GraphQL_FragmentGenerator.deriveRefsQueryField(
390
+ ~listFieldName="Catalog_Products",
391
+ ~returnTypeName="Catalog_Product",
392
+ ),
393
+ )->toEqual(" Catalog_ProductsRefs(ids: [ID!]!): [Catalog_ProductRef!]!")
394
+ )
395
+
396
+ // Every assertion above reads a `derive*` helper directly, which is how a door
397
+ // that no `generate` call emitted still passed them all: the SDL both AppSync
398
+ // APIs are built from comes from `generate`, while the adapter provisions a
399
+ // `<list>Refs` resolver per queryable on the premise that it is declared there.
400
+ // AppSync rejects a resolver for a field its schema does not have, so the
401
+ // mismatch surfaced as a failed deploy rather than a missing feature.
402
+ testSync("emits the door from generate, not only from the helper", () =>
403
+ expect((
404
+ queryFor(refDoorFragment(), "Catalog_ProductsRefs(ids: [ID!]!)")->Option.isSome,
405
+ typeDefFor(refDoorFragment(), "type Catalog_ProductRef")->Option.isSome,
406
+ ))->toEqual((true, true))
407
+ )
408
+
409
+ // The condition the resolver side uses, mirrored: a composite-key view gets no
410
+ // door, because the read behind it is a BatchGetItem that would need both keys.
411
+ // Emitting the field for one would restore the same mismatch, reversed.
412
+ testSync("withholds the door from a composite-key view", () =>
413
+ expect((
414
+ queryFor(refDoorFragment(~subIdField="lineNo"), "Catalog_ProductsRefs")->Option.isSome,
415
+ typeDefFor(refDoorFragment(~subIdField="lineNo"), "type Catalog_ProductRef")->Option.isSome,
416
+ ))->toEqual((false, false))
417
+ )
418
+
419
+ // The dead end this closes: the archive toggle put retired rows on screen and
420
+ // clicking one read a door that refused them to every caller alive, elevated
421
+ // included, because there was no way to ask.
422
+ testSync("lets the single-entity and by-ids doors be asked for the archive", () =>
423
+ expect((
424
+ GraphQL_FragmentGenerator.deriveObjectQueryField(
425
+ ~singleFieldName="Catalog_Product",
426
+ ~typeName="Catalog_Product",
427
+ )->String.includes("includeRetired: Boolean"),
428
+ GraphQL_FragmentGenerator.deriveByIdsQueryField(
429
+ ~listFieldName="Catalog_Products",
430
+ ~returnTypeName="Catalog_Product",
431
+ )->String.includes("includeRetired: Boolean"),
432
+ ))->toEqual((true, true))
433
+ )
434
+
435
+ // A sub-id read keeps its sort-key argument beside the new one; dropping it
436
+ // would silently turn a two-key door into a one-key one.
437
+ testSync("keeps the sub-id argument when the view has one", () =>
438
+ expect(
439
+ GraphQL_FragmentGenerator.deriveObjectQueryField(
440
+ ~singleFieldName="Ordering_OrderLine",
441
+ ~typeName="Ordering_OrderLine",
442
+ ~subIdField="lineNo",
443
+ ),
444
+ )->toEqual(" Ordering_OrderLine(id: ID!, lineNo: String!, includeRetired: Boolean): Ordering_OrderLine")
445
+ )
446
+ })
447
+
448
+ // The by-index door is asserted whole rather than by `String.includes`, because
449
+ // what was wrong with it was never a missing substring: the field declared an
450
+ // `id` its resolver did not read and omitted the key its resolver did, so every
451
+ // part of the signature is load-bearing.
452
+ describe("the by-index door", () => {
453
+ let index = (~index, ~idField=?, ~subIdField=?): Reventless.ReadModel.indexConfig => {
454
+ index,
455
+ type_: "S",
456
+ idField: ?idField,
457
+ subIdField: ?subIdField,
458
+ projectionType: ALL,
459
+ }
460
+
461
+ testSync("takes the index value it filters on, and can be asked for the archive", () =>
462
+ expect(
463
+ GraphQL_FragmentGenerator.deriveIndexQueryField(
464
+ ~singleFieldName="Catalog_Product",
465
+ ~indexConfig=index(~index="categoryId"),
466
+ ~connectionTypeName="Catalog_ProductConnection",
467
+ ),
468
+ )->toEqual(
469
+ " Catalog_ProductByCategoryId(categoryId: String!, first: Int, after: String, last: Int, before: String, includeRetired: Boolean): Catalog_ProductConnection!",
470
+ )
471
+ )
472
+
473
+ // A named index has two different strings in play — `byOwner` names the index,
474
+ // `ownerId` names the column. The field is named after the first and keyed on
475
+ // the second, and swapping them is how the local adapter came to offer an
476
+ // argument the row had no field for.
477
+ testSync("names the field after the index and keys it on the column", () =>
478
+ expect(
479
+ GraphQL_FragmentGenerator.deriveIndexQueryField(
480
+ ~singleFieldName="Ordering_Order",
481
+ ~indexConfig=index(~index="byOwner", ~idField="ownerId"),
482
+ ~connectionTypeName="Ordering_OrderConnection",
483
+ ),
484
+ )->toEqual(
485
+ " Ordering_OrderByOwner(ownerId: String!, first: Int, after: String, last: Int, before: String, includeRetired: Boolean): Ordering_OrderConnection!",
486
+ )
487
+ )
488
+
489
+ // Optional, unlike the partition argument: naming the index value is the point
490
+ // of the door, narrowing to one sort value is a refinement.
491
+ testSync("offers the sort key the sort template has always read", () =>
492
+ expect(
493
+ GraphQL_FragmentGenerator.deriveIndexQueryField(
494
+ ~singleFieldName="Ordering_Order",
495
+ ~indexConfig=index(~index="byCustomer", ~idField="customerId", ~subIdField="placedAt"),
496
+ ~connectionTypeName="Ordering_OrderConnection",
497
+ ),
498
+ )->toEqual(
499
+ " Ordering_OrderByCustomer(customerId: String!, placedAt: String, first: Int, after: String, last: Int, before: String, includeRetired: Boolean): Ordering_OrderConnection!",
500
+ )
501
+ )
502
+
503
+ // One derivation, called by every backend. The local adapter used to spell the
504
+ // name out itself and produced `XByByOwner` where this produces `XByOwner`.
505
+ testSync("drops a leading `by` exactly once", () =>
506
+ expect((
507
+ GraphQL_FragmentGenerator.indexQueryFieldName(~singleFieldName="X", ~index="byOwner"),
508
+ GraphQL_FragmentGenerator.indexQueryFieldName(~singleFieldName="X", ~index="ownerId"),
509
+ GraphQL_FragmentGenerator.indexQueryFieldName(~singleFieldName="X", ~index="by"),
510
+ ))->toEqual(("XByOwner", "XByOwnerId", "XByBy"))
511
+ )
512
+ })
513
+
514
+ // ── @internal: a field that is not on the GraphQL surface ────────────────────
515
+ //
516
+ // A read model's `@schema type state` is otherwise simultaneously the storage
517
+ // shape and the API shape. `@internal` separates them: the field stays in the
518
+ // record and in storage, and every generated object type drops it.
519
+ //
520
+ // The declaration is read off the schema rather than passed in, which is the
521
+ // point — the alternative is a hand-written exclude list beside the query entry,
522
+ // kept in step with the record by whoever next edits it. That is exactly what
523
+ // `PluginBaseFragment` maintained and what this replaces.
524
+ //
525
+ // The fixture is `PsInternalFieldsReadModel`, a spec file: the annotation machinery
526
+ // runs under `@@reventless.spec`.
527
+
528
+ describe("@internal keeps a field off the generated type", () => {
529
+ let stateSchema = PsInternalFieldsReadModel.stateSchema->S.castToUnknown
530
+
531
+ let types = (~excludeFields=?) =>
532
+ GraphQL_FragmentGenerator.deriveObjectTypeWithNested(
533
+ ~typeName="Platform_Widget",
534
+ ~excludeFields?,
535
+ stateSchema,
536
+ )->Array.join("\n")
537
+
538
+ testSync("the declared fields are absent", () =>
539
+ expect((
540
+ types()->String.includes("eventCollector"),
541
+ types()->String.includes("extensionNames"),
542
+ ))->toEqual((false, false))
543
+ )
544
+
545
+ testSync("and everything else is untouched", () =>
546
+ expect((types()->String.includes("name"), types()->String.includes("itemId")))->toEqual((
547
+ true,
548
+ true,
549
+ ))
550
+ )
551
+
552
+ // The caller's own exclusions still apply. The two lists answer different
553
+ // questions — one is a decision made where the entry is assembled, the other a
554
+ // declaration on the field — so the marker joins the list rather than
555
+ // replacing it.
556
+ testSync("a caller's excludeFields still applies alongside", () => {
557
+ let sdl = types(~excludeFields=["name"])
558
+ expect((sdl->String.includes("name"), sdl->String.includes("eventCollector")))->toEqual((
559
+ false,
560
+ false,
561
+ ))
562
+ })
563
+
564
+ // The published state schema and the SDL have to agree, because AutoUI builds
565
+ // its list query from the former and sends it against the latter. This is the
566
+ // pairing that used to need two hand-maintained lists to hold.
567
+ testSync("the published state schema drops them too", () => {
568
+ let json = SuryToJsonSchema.deriveObjectSchema(stateSchema)->JSON.stringify
569
+ expect((json->String.includes("eventCollector"), json->String.includes("name")))->toEqual((
570
+ false,
571
+ true,
572
+ ))
573
+ })
574
+ })