@reventlessdev/reventless-core 3.0.0-alpha.222 → 3.0.0-alpha.224

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 (30) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/package.json +7 -7
  3. package/src/admin/Platform_Admin_Structure.res +10 -0
  4. package/src/admin/Platform_Admin_Structure.res.mjs +6 -1
  5. package/src/admin/Platform_ComponentDefinitionsApi.res +33 -2
  6. package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +18 -2
  7. package/src/components/Api/Api_Ids.res +63 -0
  8. package/src/components/Api/Api_Ids.res.mjs +43 -0
  9. package/src/components/Api/GraphQL_FragmentGenerator.res +121 -28
  10. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +97 -27
  11. package/src/components/Api/QueryDbListQuery.res +13 -5
  12. package/src/components/Api/QueryDbListQuery.res.mjs +5 -3
  13. package/src/components/Api/SchemaType.res +36 -2
  14. package/src/components/Api/SchemaType.res.mjs +31 -10
  15. package/src/plugin/component/Plugin_Structure.res +41 -24
  16. package/src/plugin/component/Plugin_Structure.res.mjs +25 -18
  17. package/tests/admin/Platform_ComponentDefinitionsApiTest.res +118 -7
  18. package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +168 -7
  19. package/tests/admin/Platform_PluginStructuresApiTest.res +10 -0
  20. package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +23 -2
  21. package/tests/api/Api_IdsTest.res +143 -0
  22. package/tests/api/Api_IdsTest.res.mjs +97 -0
  23. package/tests/api/GraphQL_FragmentGeneratorTest.res +169 -0
  24. package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +179 -0
  25. package/tests/plugin/PluginStructureTest.res +159 -0
  26. package/tests/plugin/PluginStructureTest.res.mjs +160 -6
  27. package/tests/plugin/StateChangeSlice/PsReserveStock.res +44 -0
  28. package/tests/plugin/StateChangeSlice/PsReserveStock.res.mjs +67 -0
  29. package/tests/plugin/StateViewSlice/PsCategoriesView.res +17 -0
  30. package/tests/plugin/StateViewSlice/PsCategoriesView.res.mjs +57 -0
@@ -5,20 +5,24 @@ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
5
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
6
  import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
7
7
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
+ import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
8
9
  import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
9
10
  import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/types/DateTime.res.mjs";
10
11
  import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
11
12
  import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
13
+ import * as Api_Naming$ReventlessCore from "../../src/components/Api/Api_Naming.res.mjs";
12
14
  import * as PsShipOrder$ReventlessCore from "./StateChangeSlice/PsShipOrder.res.mjs";
13
15
  import * as PsOrdersView$ReventlessCore from "./StateViewSlice/PsOrdersView.res.mjs";
14
16
  import * as PsPlaceOrder$ReventlessCore from "./StateChangeSlice/PsPlaceOrder.res.mjs";
15
17
  import * as PsChangePhoto$ReventlessCore from "./StateChangeSlice/PsChangePhoto.res.mjs";
16
18
  import * as CapabilityManifest$Reventless from "@reventlessdev/reventless-spec/src/components/CapabilityManifest.res.mjs";
19
+ import * as PsReserveStock$ReventlessCore from "./StateChangeSlice/PsReserveStock.res.mjs";
17
20
  import * as PsUploadAvatar$ReventlessCore from "./StateChangeSlice/PsUploadAvatar.res.mjs";
18
21
  import * as PsAnnotatedView$ReventlessCore from "./StateViewSlice/PsAnnotatedView.res.mjs";
19
22
  import * as PsAttachInvoice$ReventlessCore from "./StateChangeSlice/PsAttachInvoice.res.mjs";
20
23
  import * as PsCustomersView$ReventlessCore from "./StateViewSlice/PsCustomersView.res.mjs";
21
24
  import * as Plugin_Structure$ReventlessCore from "../../src/plugin/component/Plugin_Structure.res.mjs";
25
+ import * as PsCategoriesView$ReventlessCore from "./StateViewSlice/PsCategoriesView.res.mjs";
22
26
  import * as Capability_Inference$ReventlessCore from "../../src/plugin/component/Capability_Inference.res.mjs";
23
27
  import * as PsAvailableProductsView$ReventlessCore from "./StateViewSlice/PsAvailableProductsView.res.mjs";
24
28
 
@@ -186,6 +190,32 @@ let PsAnnotatedViewSlice = {
186
190
  make: make$5
187
191
  };
188
192
 
193
+ let Projection$4 = {
194
+ project: PsCategoriesView$ReventlessCore.project,
195
+ moduleUrl: PsCategoriesView$ReventlessCore.moduleUrl
196
+ };
197
+
198
+ function make$6(param, param$1, param$2) {
199
+ return 0;
200
+ }
201
+
202
+ let PsCategoriesViewSlice_Spec = {
203
+ name: PsCategoriesView$ReventlessCore.name,
204
+ moduleUrl: PsCategoriesView$ReventlessCore.moduleUrl,
205
+ stateSchema: PsCategoriesView$ReventlessCore.stateSchema,
206
+ consumedEventSchema: PsCategoriesView$ReventlessCore.consumedEventSchema,
207
+ config: PsCategoriesView$ReventlessCore.config,
208
+ subIdConfig: undefined,
209
+ authorization: PsCategoriesView$ReventlessCore.authorization,
210
+ visibility: PsCategoriesView$ReventlessCore.visibility
211
+ };
212
+
213
+ let PsCategoriesViewSlice = {
214
+ Spec: PsCategoriesViewSlice_Spec,
215
+ Projection: Projection$4,
216
+ make: make$6
217
+ };
218
+
189
219
  let Behavior$2 = {
190
220
  initialState: false,
191
221
  evolve: PsAttachInvoice$ReventlessCore.evolve,
@@ -193,7 +223,7 @@ let Behavior$2 = {
193
223
  moduleUrl: PsAttachInvoice$ReventlessCore.moduleUrl
194
224
  };
195
225
 
196
- function make$6(param, param$1, param$2, param$3, param$4, param$5) {
226
+ function make$7(param, param$1, param$2, param$3, param$4, param$5) {
197
227
  return 0;
198
228
  }
199
229
 
@@ -213,7 +243,7 @@ let PsAttachInvoiceSlice = {
213
243
  Spec: PsAttachInvoiceSlice_Spec,
214
244
  Behavior: Behavior$2,
215
245
  isAsync: false,
216
- make: make$6
246
+ make: make$7
217
247
  };
218
248
 
219
249
  let Behavior$3 = {
@@ -223,7 +253,7 @@ let Behavior$3 = {
223
253
  moduleUrl: PsUploadAvatar$ReventlessCore.moduleUrl
224
254
  };
225
255
 
226
- function make$7(param, param$1, param$2, param$3, param$4, param$5) {
256
+ function make$8(param, param$1, param$2, param$3, param$4, param$5) {
227
257
  return 0;
228
258
  }
229
259
 
@@ -243,7 +273,7 @@ let PsUploadAvatarSlice = {
243
273
  Spec: PsUploadAvatarSlice_Spec,
244
274
  Behavior: Behavior$3,
245
275
  isAsync: false,
246
- make: make$7
276
+ make: make$8
247
277
  };
248
278
 
249
279
  let Behavior$4 = {
@@ -253,7 +283,7 @@ let Behavior$4 = {
253
283
  moduleUrl: PsChangePhoto$ReventlessCore.moduleUrl
254
284
  };
255
285
 
256
- function make$8(param, param$1, param$2, param$3, param$4, param$5) {
286
+ function make$9(param, param$1, param$2, param$3, param$4, param$5) {
257
287
  return 0;
258
288
  }
259
289
 
@@ -273,7 +303,37 @@ let PsChangePhotoSlice = {
273
303
  Spec: PsChangePhotoSlice_Spec,
274
304
  Behavior: Behavior$4,
275
305
  isAsync: false,
276
- make: make$8
306
+ make: make$9
307
+ };
308
+
309
+ let Behavior$5 = {
310
+ initialState: false,
311
+ evolve: PsReserveStock$ReventlessCore.evolve,
312
+ decide: PsReserveStock$ReventlessCore.decide,
313
+ moduleUrl: PsReserveStock$ReventlessCore.moduleUrl
314
+ };
315
+
316
+ function make$10(param, param$1, param$2, param$3, param$4, param$5) {
317
+ return 0;
318
+ }
319
+
320
+ let PsReserveStockSlice_Spec = {
321
+ name: PsReserveStock$ReventlessCore.name,
322
+ moduleUrl: PsReserveStock$ReventlessCore.moduleUrl,
323
+ Id: Id$Reventless.$$String,
324
+ consumedEventSchema: PsReserveStock$ReventlessCore.consumedEventSchema,
325
+ errorSchema: PsReserveStock$ReventlessCore.errorSchema,
326
+ eventSchema: PsReserveStock$ReventlessCore.eventSchema,
327
+ commandSchema: PsReserveStock$ReventlessCore.commandSchema,
328
+ commandAuthorization: PsReserveStock$ReventlessCore.commandAuthorization,
329
+ readConsistency: PsReserveStock$ReventlessCore.readConsistency
330
+ };
331
+
332
+ let PsReserveStockSlice = {
333
+ Spec: PsReserveStockSlice_Spec,
334
+ Behavior: Behavior$5,
335
+ isAsync: false,
336
+ make: make$10
277
337
  };
278
338
 
279
339
  let structure = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, [
@@ -678,6 +738,54 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
678
738
  globalThis.expect(stated).toEqual(true);
679
739
  });
680
740
  });
741
+ globalThis.describe("singleQueryField — the singular the schema actually serves", () => {
742
+ let withIrregularPlural = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, [PsCategoriesViewSlice], [], undefined, undefined, undefined, undefined, undefined, undefined);
743
+ let categories = withIrregularPlural.stateViewSlices[0];
744
+ globalThis.test("an `-ies` plural publishes the `y` singular, not a truncation", () => {
745
+ globalThis.expect([
746
+ categories.queryField,
747
+ categories.singleQueryField
748
+ ]).toEqual([
749
+ "TestPlugin_Categories",
750
+ "TestPlugin_Category"
751
+ ]);
752
+ });
753
+ globalThis.test("every built def states the name Api_Naming returned", () => {
754
+ let stated = structure.stateViewSlices.every(q => Primitive_object.equal(q.singleQueryField, Api_Naming$ReventlessCore.queryFieldNamesForStateView("TestPlugin", q.name, undefined).singleFieldName));
755
+ globalThis.expect(stated).toEqual(true);
756
+ });
757
+ });
758
+ globalThis.describe("idField / idFieldSource — the key and its provenance", () => {
759
+ let keyOf = q => [
760
+ q.idField,
761
+ q.idFieldSource
762
+ ];
763
+ let withCategories = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, [PsCategoriesViewSlice], [], undefined, undefined, undefined, undefined, undefined, undefined);
764
+ globalThis.test("a name-matching `*Id` field is a guess the consumer can also make", () => {
765
+ globalThis.expect(keyOf(structure.stateViewSlices[0])).toEqual([
766
+ "orderId",
767
+ "convention"
768
+ ]);
769
+ });
770
+ globalThis.test("a sole `*Id` field is a fact only this side can see", () => {
771
+ globalThis.expect(keyOf(structure.stateViewSlices[1])).toEqual([
772
+ "productId",
773
+ "sole"
774
+ ]);
775
+ });
776
+ globalThis.test("an `-ies` component name singularises to its key", () => {
777
+ globalThis.expect(keyOf(withCategories.stateViewSlices[0])).toEqual([
778
+ "categoryId",
779
+ "convention"
780
+ ]);
781
+ });
782
+ globalThis.test("a declared @id is used where inference would decline", () => {
783
+ globalThis.expect(keyOf(structure.stateViewSlices[3])).toEqual([
784
+ "itemId",
785
+ "annotation"
786
+ ]);
787
+ });
788
+ });
681
789
  globalThis.describe("statusField — the shape rule", () => {
682
790
  globalThis.test("a `status` field holding a closed set of values is the lifecycle field", () => {
683
791
  let schema = S.schema(s => ({
@@ -962,6 +1070,50 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
962
1070
  ]);
963
1071
  });
964
1072
  });
1073
+ globalThis.describe("plural references reach the manifest", () => {
1074
+ let withPluralRefs = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsReserveStockSlice], undefined, undefined, undefined, undefined, undefined, undefined);
1075
+ let slice = withPluralRefs.stateChangeSlices[0];
1076
+ let referencesOf = refs => refs.map(param => [
1077
+ param.fieldName,
1078
+ param.entity
1079
+ ]);
1080
+ globalThis.test("a command collects its array refs alongside its scalar one", () => {
1081
+ let cmd = slice.commands[0];
1082
+ globalThis.expect(referencesOf(cmd.references)).toEqual([
1083
+ [
1084
+ "customerId",
1085
+ "Customers"
1086
+ ],
1087
+ [
1088
+ "productIds",
1089
+ "AvailableProducts"
1090
+ ],
1091
+ [
1092
+ "warehouseIds",
1093
+ "Warehouses"
1094
+ ]
1095
+ ]);
1096
+ });
1097
+ globalThis.test("an optional array ref is collected under the field's own name", () => {
1098
+ let cmd = slice.commands[0];
1099
+ globalThis.expect(cmd.references.find(param => param.fieldName === "warehouseIds")).toEqual({
1100
+ fieldName: "warehouseIds",
1101
+ entity: "Warehouses",
1102
+ plugin: undefined
1103
+ });
1104
+ });
1105
+ globalThis.test("an event collects its array ref too", () => {
1106
+ let evt = slice.events[0];
1107
+ globalThis.expect(referencesOf(evt.references)).toEqual([[
1108
+ "productIds",
1109
+ "AvailableProducts"
1110
+ ]]);
1111
+ });
1112
+ globalThis.test("the declared entity wins over the one the field name suggests", () => {
1113
+ let cmd = slice.commands[0];
1114
+ globalThis.expect(Stdlib_Option.map(cmd.references.find(param => param.fieldName === "productIds"), param => param.entity)).toEqual("AvailableProducts");
1115
+ });
1116
+ });
965
1117
  });
966
1118
 
967
1119
  export {
@@ -971,9 +1123,11 @@ export {
971
1123
  PsAvailableProductsViewSlice,
972
1124
  PsCustomersViewSlice,
973
1125
  PsAnnotatedViewSlice,
1126
+ PsCategoriesViewSlice,
974
1127
  PsAttachInvoiceSlice,
975
1128
  PsUploadAvatarSlice,
976
1129
  PsChangePhotoSlice,
1130
+ PsReserveStockSlice,
977
1131
  structure,
978
1132
  }
979
1133
  /* structure Not a pure module */
@@ -0,0 +1,44 @@
1
+ // Test fixture spec where every reference carrier is PLURAL.
2
+ //
3
+ // `Reference.to_` returns an *element* schema, so the ppx annotates the `string`
4
+ // inside `array<string>` and the field's own schema carries no marker. A walk
5
+ // that asks the field directly finds nothing and drops the declaration — and a
6
+ // dropped `@ref` is not the same as an absent one: the consuming side falls back
7
+ // to a naming heuristic and resolves the field to whatever entity the name
8
+ // suggests, so the author's declaration is both ignored and reported as missing.
9
+ //
10
+ // The command declares the scalar and plural forms side by side so a regression
11
+ // shows up as one of the two disappearing rather than as an empty list, and
12
+ // `warehouseIds?` covers the plural-inside-optional shape, where the array sits
13
+ // one wrapper further down still.
14
+
15
+ @@reventless.spec("ReserveStock")
16
+
17
+ type state = bool
18
+ let initialState = false
19
+
20
+ @schema
21
+ type consumedEvent = StockReserved({reservationId: string})
22
+
23
+ let evolve = (_state, _event) => true
24
+
25
+ @schema
26
+ type command =
27
+ | ReserveStock({
28
+ @partitionTag reservationId: string,
29
+ @ref("Customers") customerId: string,
30
+ @ref("AvailableProducts") productIds: array<string>,
31
+ @ref("Warehouses") warehouseIds?: array<string>,
32
+ })
33
+
34
+ @schema
35
+ type error = OutOfStock
36
+
37
+ @schema
38
+ type event =
39
+ StockReserved({
40
+ @partitionTag reservationId: string,
41
+ @ref("AvailableProducts") productIds: array<string>,
42
+ })
43
+
44
+ let decide = (_state, _command): result<array<event>, error> => Ok([])
@@ -0,0 +1,67 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
5
+ import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
6
+
7
+ let consumedEventSchema = S.schema(s => ({
8
+ TAG: "StockReserved",
9
+ reservationId: s.m(DcbTag$Reventless.string)
10
+ }));
11
+
12
+ function evolve(_state, _event) {
13
+ return true;
14
+ }
15
+
16
+ let commandSchema = S.schema(s => ({
17
+ TAG: "ReserveStock",
18
+ reservationId: s.m(DcbTag$Reventless.partition),
19
+ customerId: s.m(Reference$Reventless.to_(undefined, undefined, "Customers")),
20
+ productIds: s.m(S.array(Reference$Reventless.to_(undefined, "productId", "AvailableProducts"))),
21
+ warehouseIds: s.m(S.option(S.array(Reference$Reventless.to_(undefined, "warehouseId", "Warehouses"))))
22
+ }));
23
+
24
+ let errorSchema = S.literal("OutOfStock");
25
+
26
+ let eventSchema = S.schema(s => ({
27
+ TAG: "StockReserved",
28
+ reservationId: s.m(DcbTag$Reventless.partition),
29
+ productIds: s.m(S.array(Reference$Reventless.to_(undefined, "productId", "AvailableProducts")))
30
+ }));
31
+
32
+ function decide(_state, _command) {
33
+ return {
34
+ TAG: "Ok",
35
+ _0: []
36
+ };
37
+ }
38
+
39
+ function commandAuthorization(param) {
40
+ return "AllowAuthenticated";
41
+ }
42
+
43
+ let name = "ReserveStock";
44
+
45
+ let Id;
46
+
47
+ let initialState = false;
48
+
49
+ let moduleUrl = "@reventlessdev/reventless-core/tests/plugin/StateChangeSlice/PsReserveStock.res.mjs";
50
+
51
+ let readConsistency = "EscalateOnRetry";
52
+
53
+ export {
54
+ name,
55
+ Id,
56
+ initialState,
57
+ consumedEventSchema,
58
+ evolve,
59
+ commandSchema,
60
+ errorSchema,
61
+ eventSchema,
62
+ decide,
63
+ moduleUrl,
64
+ commandAuthorization,
65
+ readConsistency,
66
+ }
67
+ /* consumedEventSchema Not a pure module */
@@ -0,0 +1,17 @@
1
+ // Test fixture for the published singular query field.
2
+ // "Categories" is the name a naive "strip a trailing s" derivation gets wrong
3
+ // (`Categorie`), so it is the one that proves `singleQueryField` comes from
4
+ // `Api_Naming` rather than from a rule re-invented at the consumer.
5
+
6
+ @@reventless.spec("Categories")
7
+
8
+ @schema
9
+ type consumedEvent = CategoryAdded({categoryId: string, name: string})
10
+
11
+ @schema
12
+ type state = {categoryId: string, name: string}
13
+
14
+ let project = ({event}: Reventless.StateViewSlice.consumed<consumedEvent>) =>
15
+ switch event {
16
+ | CategoryAdded({categoryId, name}) => [Set(categoryId, {categoryId, name})]
17
+ }
@@ -0,0 +1,57 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
5
+ import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
6
+
7
+ let consumedEventSchema = S.schema(s => ({
8
+ TAG: "CategoryAdded",
9
+ categoryId: s.m(DcbTag$Reventless.string),
10
+ name: s.m(S.string)
11
+ }));
12
+
13
+ let stateSchema = S.schema(s => ({
14
+ categoryId: s.m(S.string),
15
+ name: s.m(S.string)
16
+ }));
17
+
18
+ function project(param) {
19
+ let event = param.event;
20
+ let categoryId = event.categoryId;
21
+ return [{
22
+ TAG: "Set",
23
+ _0: categoryId,
24
+ _1: {
25
+ categoryId: categoryId,
26
+ name: event.name
27
+ }
28
+ }];
29
+ }
30
+
31
+ let config = ReadModel$Reventless.config(undefined, undefined, undefined);
32
+
33
+ let name = "Categories";
34
+
35
+ let Id;
36
+
37
+ let subIdConfig;
38
+
39
+ let moduleUrl = "@reventlessdev/reventless-core/tests/plugin/StateViewSlice/PsCategoriesView.res.mjs";
40
+
41
+ let authorization = "AllowAuthenticated";
42
+
43
+ let visibility = "Public";
44
+
45
+ export {
46
+ name,
47
+ Id,
48
+ consumedEventSchema,
49
+ stateSchema,
50
+ project,
51
+ config,
52
+ subIdConfig,
53
+ moduleUrl,
54
+ authorization,
55
+ visibility,
56
+ }
57
+ /* consumedEventSchema Not a pure module */