@reventlessdev/reventless-core 3.0.0-alpha.155 → 3.0.0-alpha.157

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 (75) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +49 -150
  3. package/package.json +13 -13
  4. package/src/admin/AdminApi.res +80 -10
  5. package/src/admin/AdminApi.res.mjs +45 -7
  6. package/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry.res +65 -0
  7. package/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry.res.mjs +87 -0
  8. package/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry_Behavior.res +58 -0
  9. package/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry_Behavior.res.mjs +135 -0
  10. package/src/admin/ApiFragmentRegistry/StateViewSlice/ApiFragments.res +44 -0
  11. package/src/admin/ApiFragmentRegistry/StateViewSlice/ApiFragments.res.mjs +74 -0
  12. package/src/admin/ApiFragmentRegistry/StateViewSlice/ApiFragments_Projection.res +50 -0
  13. package/src/admin/ApiFragmentRegistry/StateViewSlice/ApiFragments_Projection.res.mjs +80 -0
  14. package/src/admin/Platform_Admin.res +37 -1
  15. package/src/admin/Platform_Admin.res.mjs +18 -5
  16. package/src/admin/Platform_ApiFragmentsApi.res +32 -0
  17. package/src/admin/Platform_ApiFragmentsApi.res.mjs +52 -0
  18. package/src/admin/Platform_UIFragmentsApi.res +1 -1
  19. package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res +50 -0
  20. package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res.mjs +70 -0
  21. package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry_Behavior.res +33 -0
  22. package/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry_Behavior.res.mjs +80 -0
  23. package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments.res +33 -0
  24. package/src/admin/{UIFragmentRegistryReadModelSpec.res.mjs → UiFragmentRegistry/StateViewSlice/UiFragments.res.mjs} +24 -2
  25. package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res +34 -0
  26. package/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res.mjs +54 -0
  27. package/src/components/Api/Api_Naming.res +1 -1
  28. package/src/components/Api/GraphQL_FragmentGenerator.res +7 -11
  29. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +8 -22
  30. package/src/components/Api/GraphQL_PushPlanner.res +65 -0
  31. package/src/components/Api/GraphQL_PushPlanner.res.mjs +46 -0
  32. package/src/components/Api/GraphQL_Stitcher.res +72 -1
  33. package/src/components/Api/GraphQL_Stitcher.res.mjs +53 -2
  34. package/src/components/Dcb/Dcb_Builder.res +16 -6
  35. package/src/components/Dcb/Dcb_Builder.res.mjs +10 -7
  36. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +6 -1
  37. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +1 -1
  38. package/src/plugin/api/PluginBaseFragment.res +5 -6
  39. package/src/plugin/api/PluginBaseFragment.res.mjs +0 -1
  40. package/src/plugin/component/Plugin_Builder.res +4 -1
  41. package/src/plugin/component/Plugin_Builder.res.mjs +7 -6
  42. package/src/plugin/component/Plugin_Helpers.res +30 -0
  43. package/src/plugin/component/Plugin_Helpers.res.mjs +13 -3
  44. package/src/plugin/component/Plugin_SubscriptionSchema.res +19 -9
  45. package/src/plugin/component/Plugin_SubscriptionSchema.res.mjs +23 -14
  46. package/src/plugin/connect/PluginConnectExtension_Mapping.res +28 -6
  47. package/src/plugin/connect/PluginConnectExtension_Mapping.res.mjs +34 -14
  48. package/src/plugin/connect/PluginExtensionPoint_Builder.res +7 -1
  49. package/src/plugin/connect/PluginExtensionPoint_Builder.res.mjs +5 -1
  50. package/src/plugin/connect/PluginExtensionPoint_Plugin.res +3 -1
  51. package/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs +4 -2
  52. package/src/plugin/connect/PluginExtensionPoint_UiFragment.res +52 -0
  53. package/src/plugin/connect/PluginExtensionPoint_UiFragment.res.mjs +74 -0
  54. package/src/plugin/lifecycle/PluginBehavior.res +9 -51
  55. package/src/plugin/lifecycle/PluginBehavior.res.mjs +39 -68
  56. package/src/plugin/lifecycle/PluginSpec.res +0 -18
  57. package/src/plugin/lifecycle/PluginSpec.res.mjs +0 -30
  58. package/src/plugin/lifecycle/PluginsProjection.res +2 -6
  59. package/src/plugin/lifecycle/PluginsProjection.res.mjs +0 -2
  60. package/src/plugin/lifecycle/PluginsReadModelSpec.res +0 -2
  61. package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +0 -1
  62. package/tests/admin/Platform_ApiFragmentsApiTest.res +66 -0
  63. package/tests/admin/Platform_ApiFragmentsApiTest.res.mjs +68 -0
  64. package/tests/admin/Platform_UIFragmentsApiTest.res +1 -1
  65. package/tests/api/GraphQL_PushPlannerTest.res +62 -0
  66. package/tests/api/GraphQL_PushPlannerTest.res.mjs +80 -0
  67. package/tests/api/GraphQL_StitcherTest.res +63 -0
  68. package/tests/api/GraphQL_StitcherTest.res.mjs +75 -0
  69. package/tests/logger/LogFormatTest.res +1 -2
  70. package/tests/logger/LogFormatTest.res.mjs +1 -2
  71. package/tests/message/MessageTest.res +0 -3
  72. package/tests/message/MessageTest.res.mjs +0 -3
  73. package/src/admin/UIFragmentRegistryProjection.res +0 -43
  74. package/src/admin/UIFragmentRegistryProjection.res.mjs +0 -81
  75. package/src/admin/UIFragmentRegistryReadModelSpec.res +0 -13
@@ -0,0 +1,52 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
5
+ import * as Plugin$ReventlessCore from "../plugin/component/Plugin.res.mjs";
6
+
7
+ let sdlTypes = [`type Platform_ApiFragmentEntry {\n pluginId: String!\n apiTarget: String!\n pushStatus: String!\n pushMessage: String!\n pushedAt: String!\n registeredAt: String!\n updatedAt: String!\n}`];
8
+
9
+ let queryFieldName = "Platform_ApiFragments";
10
+
11
+ let sdlQueryField = ` ` + queryFieldName + `: [Platform_ApiFragmentEntry!]!`;
12
+
13
+ function encodeApiFragmentEntry(entry) {
14
+ return Object.fromEntries([
15
+ [
16
+ "pluginId",
17
+ Plugin$ReventlessCore.name(entry.pluginId)
18
+ ],
19
+ [
20
+ "apiTarget",
21
+ S.reverseConvertToJsonOrThrow(entry.apiTarget, Plugin$Reventless.apiTargetSchema)
22
+ ],
23
+ [
24
+ "pushStatus",
25
+ entry.pushStatus
26
+ ],
27
+ [
28
+ "pushMessage",
29
+ entry.pushMessage
30
+ ],
31
+ [
32
+ "pushedAt",
33
+ entry.pushedAt
34
+ ],
35
+ [
36
+ "registeredAt",
37
+ entry.registeredAt
38
+ ],
39
+ [
40
+ "updatedAt",
41
+ entry.updatedAt
42
+ ]
43
+ ]);
44
+ }
45
+
46
+ export {
47
+ sdlTypes,
48
+ queryFieldName,
49
+ sdlQueryField,
50
+ encodeApiFragmentEntry,
51
+ }
52
+ /* S Not a pure module */
@@ -44,7 +44,7 @@ let encodePage = (p: pageManifestEntry): JSON.t =>
44
44
  ("requiredAccess", p.requiredAccess->Option.mapOr(JSON.Encode.null, JSON.Encode.string)),
45
45
  ])->JSON.Encode.object
46
46
 
47
- let encodeUIFragmentEntry = (entry: UIFragmentRegistryReadModelSpec.state): JSON.t =>
47
+ let encodeUIFragmentEntry = (entry: UiFragments.state): JSON.t =>
48
48
  Dict.fromArray([
49
49
  ("pluginId", JSON.Encode.string(Plugin.name(entry.pluginId))),
50
50
  ("remoteEntryUrl", JSON.Encode.string(entry.remoteEntryUrl)),
@@ -0,0 +1,50 @@
1
+ // UiFragmentRegistry StateChangeSlice — the write side of the platform UI-fragment
2
+ // registry, extracted off the Plugin aggregate (see
3
+ // docs/plans/event-sourced-fragment-registries.md).
4
+ //
5
+ // Commands are driven by the plugin runtime's connect handshake and routed here by the
6
+ // admin PluginExtensionPoint's UI-fragment mapping (RegisterUiFragment on ConnectPlugin,
7
+ // DeregisterUiFragment on DisconnectPlugin — the disconnect schedule sends DisconnectPlugin
8
+ // on heartbeat timeout too, so timeout deregistration flows through the same path).
9
+ //
10
+ // Keyed by plugin NAME (the `pluginId` tag carries the bare name), so a single row tracks
11
+ // the current manifest across version supersession. The registry is idempotent: re-sending
12
+ // an identical manifest is a no-op, deregistering an absent fragment is a no-op — it never
13
+ // rejects a command.
14
+ @@reventless.spec
15
+
16
+ open Reventless.Plugin
17
+
18
+ // `at` is the registration timestamp, stamped from the incoming command meta by the EP mapping
19
+ // — the StateViewSlice projection has no event meta, so time must ride the payload. Inline
20
+ // records let the ppx auto-tag `pluginId` (scoping the decision read to one plugin name).
21
+ //
22
+ // Whole-command `@noApi`: this registry has NO GraphQL surface — commands are driven by the
23
+ // plugin runtime's connect/disconnect handshake and routed here by the admin
24
+ // PluginExtensionPoint mapping (AWS) / direct admin-DCB dispatch (local), never by a mutation.
25
+ // Without this marker the DCB builder would emit orphaned `Platform_RegisterUiFragment`
26
+ // resolvers against a schema that never declares those fields.
27
+ @noApi @schema
28
+ type command =
29
+ | RegisterUiFragment({pluginId: string, manifest: uiFragmentManifest, at: string})
30
+ | DeregisterUiFragment({pluginId: string})
31
+
32
+ // The registry never rejects (idempotent register/deregister); this variant is never returned.
33
+ @schema
34
+ type error = | RegistryError
35
+
36
+ @schema
37
+ type event =
38
+ | UiFragmentRegistered({pluginId: string, manifest: uiFragmentManifest, at: string})
39
+ | UiFragmentUpdated({
40
+ pluginId: string,
41
+ previousManifest: uiFragmentManifest,
42
+ newManifest: uiFragmentManifest,
43
+ at: string,
44
+ })
45
+ | UiFragmentDeregistered({pluginId: string})
46
+
47
+ // This slice reads exactly the events it writes, so consumedEvent IS event — one inline event
48
+ // type, no duplicate-constructor collision, ppx auto-tags `pluginId` on both roles.
49
+ @schema
50
+ type consumedEvent = event
@@ -0,0 +1,70 @@
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 Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
6
+ import * as Api$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Api.res.mjs";
7
+
8
+ let commandSchema = S.union([
9
+ S.schema(s => ({
10
+ TAG: "RegisterUiFragment",
11
+ pluginId: s.m(DcbTag$Reventless.string),
12
+ manifest: s.m(Plugin$Reventless.uiFragmentManifestSchema),
13
+ at: s.m(S.string)
14
+ })),
15
+ S.schema(s => ({
16
+ TAG: "DeregisterUiFragment",
17
+ pluginId: s.m(DcbTag$Reventless.string)
18
+ }))
19
+ ]);
20
+
21
+ let errorSchema = S.literal("RegistryError");
22
+
23
+ let eventSchema = S.union([
24
+ S.schema(s => ({
25
+ TAG: "UiFragmentRegistered",
26
+ pluginId: s.m(DcbTag$Reventless.string),
27
+ manifest: s.m(Plugin$Reventless.uiFragmentManifestSchema),
28
+ at: s.m(S.string)
29
+ })),
30
+ S.schema(s => ({
31
+ TAG: "UiFragmentUpdated",
32
+ pluginId: s.m(DcbTag$Reventless.string),
33
+ previousManifest: s.m(Plugin$Reventless.uiFragmentManifestSchema),
34
+ newManifest: s.m(Plugin$Reventless.uiFragmentManifestSchema),
35
+ at: s.m(S.string)
36
+ })),
37
+ S.schema(s => ({
38
+ TAG: "UiFragmentDeregistered",
39
+ pluginId: s.m(DcbTag$Reventless.string)
40
+ }))
41
+ ]);
42
+
43
+ let commandSchema$1 = Api$ReventlessInfra.markNoApi(commandSchema);
44
+
45
+ function commandAuthorization(param) {
46
+ return "AllowAuthenticated";
47
+ }
48
+
49
+ let name = "UiFragmentRegistry";
50
+
51
+ let Id;
52
+
53
+ let consumedEventSchema = eventSchema;
54
+
55
+ let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res.mjs";
56
+
57
+ let readConsistency = "EscalateOnRetry";
58
+
59
+ export {
60
+ name,
61
+ Id,
62
+ errorSchema,
63
+ eventSchema,
64
+ consumedEventSchema,
65
+ commandSchema$1 as commandSchema,
66
+ moduleUrl,
67
+ commandAuthorization,
68
+ readConsistency,
69
+ }
70
+ /* commandSchema Not a pure module */
@@ -0,0 +1,33 @@
1
+ @@reventless.behavior
2
+
3
+ open Reventless.Plugin
4
+
5
+ // The current manifest for this plugin name, or None when no fragment is registered.
6
+ type state = option<uiFragmentManifest>
7
+
8
+ let initialState = None
9
+
10
+ let evolve = (_state, event) =>
11
+ switch event {
12
+ | UiFragmentRegistered({manifest}) => Some(manifest)
13
+ | UiFragmentUpdated({newManifest}) => Some(newManifest)
14
+ | UiFragmentDeregistered(_) => None
15
+ }
16
+
17
+ let decide = (state, command) =>
18
+ switch command {
19
+ | RegisterUiFragment({pluginId, manifest, at}) =>
20
+ switch state {
21
+ | None => Ok([UiFragmentRegistered({pluginId, manifest, at})])
22
+ // Idempotent: an unchanged re-registration (e.g. a redeploy carrying the same
23
+ // manifest, or a reconnect) emits nothing.
24
+ | Some(current) if current == manifest => Ok([])
25
+ | Some(current) =>
26
+ Ok([UiFragmentUpdated({pluginId, previousManifest: current, newManifest: manifest, at})])
27
+ }
28
+ | DeregisterUiFragment({pluginId}) =>
29
+ switch state {
30
+ | None => Ok([]) // idempotent — nothing registered
31
+ | Some(_) => Ok([UiFragmentDeregistered({pluginId: pluginId})])
32
+ }
33
+ }
@@ -0,0 +1,80 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
4
+
5
+ function evolve(_state, event) {
6
+ switch (event.TAG) {
7
+ case "UiFragmentRegistered" :
8
+ return event.manifest;
9
+ case "UiFragmentUpdated" :
10
+ return event.newManifest;
11
+ case "UiFragmentDeregistered" :
12
+ return;
13
+ }
14
+ }
15
+
16
+ function decide(state, command) {
17
+ if (command.TAG !== "RegisterUiFragment") {
18
+ if (state !== undefined) {
19
+ return {
20
+ TAG: "Ok",
21
+ _0: [{
22
+ TAG: "UiFragmentDeregistered",
23
+ pluginId: command.pluginId
24
+ }]
25
+ };
26
+ } else {
27
+ return {
28
+ TAG: "Ok",
29
+ _0: []
30
+ };
31
+ }
32
+ }
33
+ let at = command.at;
34
+ let manifest = command.manifest;
35
+ let pluginId = command.pluginId;
36
+ if (state !== undefined) {
37
+ if (Primitive_object.equal(state, manifest)) {
38
+ return {
39
+ TAG: "Ok",
40
+ _0: []
41
+ };
42
+ } else {
43
+ return {
44
+ TAG: "Ok",
45
+ _0: [{
46
+ TAG: "UiFragmentUpdated",
47
+ pluginId: pluginId,
48
+ previousManifest: state,
49
+ newManifest: manifest,
50
+ at: at
51
+ }]
52
+ };
53
+ }
54
+ } else {
55
+ return {
56
+ TAG: "Ok",
57
+ _0: [{
58
+ TAG: "UiFragmentRegistered",
59
+ pluginId: pluginId,
60
+ manifest: manifest,
61
+ at: at
62
+ }]
63
+ };
64
+ }
65
+ }
66
+
67
+ let Spec;
68
+
69
+ let initialState;
70
+
71
+ let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry_Behavior.res.mjs";
72
+
73
+ export {
74
+ Spec,
75
+ initialState,
76
+ evolve,
77
+ decide,
78
+ moduleUrl,
79
+ }
80
+ /* No side effect */
@@ -0,0 +1,33 @@
1
+ // UiFragments StateViewSlice — the read side of the platform UI-fragment registry,
2
+ // replacing UIFragmentRegistryReadModelSpec + UIFragmentRegistryProjection. Projects the
3
+ // UiFragmentRegistry slice's events (from the shared admin DcbEventLog) into the
4
+ // manifest-per-plugin table that backs the Platform_UIFragments query. The wire shape is
5
+ // owned by Platform_UIFragmentsApi.res (SDL + encoder), pinned by the golden test
6
+ // Platform_UIFragmentsApiTest.res — `state` here matches that encoder byte-for-byte.
7
+ @@reventless.spec
8
+
9
+ open Reventless.Plugin
10
+
11
+ @schema
12
+ type state = {
13
+ pluginId: string,
14
+ remoteEntryUrl: string,
15
+ panels: array<panelManifestEntry>,
16
+ pages: array<pageManifestEntry>,
17
+ registeredAt: string,
18
+ updatedAt: string,
19
+ }
20
+
21
+ // Consumes the UiFragmentRegistry slice's events (from the shared admin DcbEventLog). Inline
22
+ // records with the same constructors/fields as the producer's `event` — decodes byte-identically
23
+ // and the ppx auto-tags `pluginId`.
24
+ @schema
25
+ type consumedEvent =
26
+ | UiFragmentRegistered({pluginId: string, manifest: uiFragmentManifest, at: string})
27
+ | UiFragmentUpdated({
28
+ pluginId: string,
29
+ previousManifest: uiFragmentManifest,
30
+ newManifest: uiFragmentManifest,
31
+ at: string,
32
+ })
33
+ | UiFragmentDeregistered({pluginId: string})
@@ -1,6 +1,7 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as S from "sury/src/S.res.mjs";
4
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
4
5
  import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
5
6
  import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
6
7
 
@@ -13,15 +14,35 @@ let stateSchema = S.schema(s => ({
13
14
  updatedAt: s.m(S.string)
14
15
  }));
15
16
 
17
+ let consumedEventSchema = S.union([
18
+ S.schema(s => ({
19
+ TAG: "UiFragmentRegistered",
20
+ pluginId: s.m(DcbTag$Reventless.string),
21
+ manifest: s.m(Plugin$Reventless.uiFragmentManifestSchema),
22
+ at: s.m(S.string)
23
+ })),
24
+ S.schema(s => ({
25
+ TAG: "UiFragmentUpdated",
26
+ pluginId: s.m(DcbTag$Reventless.string),
27
+ previousManifest: s.m(Plugin$Reventless.uiFragmentManifestSchema),
28
+ newManifest: s.m(Plugin$Reventless.uiFragmentManifestSchema),
29
+ at: s.m(S.string)
30
+ })),
31
+ S.schema(s => ({
32
+ TAG: "UiFragmentDeregistered",
33
+ pluginId: s.m(DcbTag$Reventless.string)
34
+ }))
35
+ ]);
36
+
16
37
  let config = ReadModel$Reventless.config(undefined, undefined, undefined);
17
38
 
18
- let name = "UIFragmentRegistry";
39
+ let name = "UiFragments";
19
40
 
20
41
  let Id;
21
42
 
22
43
  let subIdConfig;
23
44
 
24
- let moduleUrl = "@reventlessdev/reventless-core/src/admin/UIFragmentRegistryReadModelSpec.res.mjs";
45
+ let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments.res.mjs";
25
46
 
26
47
  let authorization = "AllowAuthenticated";
27
48
 
@@ -31,6 +52,7 @@ export {
31
52
  name,
32
53
  Id,
33
54
  stateSchema,
55
+ consumedEventSchema,
34
56
  config,
35
57
  subIdConfig,
36
58
  moduleUrl,
@@ -0,0 +1,34 @@
1
+ @@reventless.projection
2
+
3
+ // No `open Reventless.Plugin`: it would shadow the state labels (panels/pages/...) brought in
4
+ // by the projection's spec open. `manifest.<field>` resolves type-directed from the event's
5
+ // uiFragmentManifest payload.
6
+ let project = event =>
7
+ switch event {
8
+ | UiFragmentRegistered({pluginId, manifest, at}) => [
9
+ Set(
10
+ pluginId,
11
+ {
12
+ pluginId,
13
+ remoteEntryUrl: manifest.remoteEntryUrl,
14
+ panels: manifest.panels,
15
+ pages: manifest.pages,
16
+ registeredAt: at,
17
+ updatedAt: at,
18
+ },
19
+ ),
20
+ ]
21
+ | UiFragmentUpdated({pluginId, newManifest, at}) => [
22
+ Update(
23
+ pluginId,
24
+ state => {
25
+ ...state,
26
+ remoteEntryUrl: newManifest.remoteEntryUrl,
27
+ panels: newManifest.panels,
28
+ pages: newManifest.pages,
29
+ updatedAt: at,
30
+ },
31
+ ),
32
+ ]
33
+ | UiFragmentDeregistered({pluginId}) => [Delete(pluginId)]
34
+ }
@@ -0,0 +1,54 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function project(event) {
5
+ switch (event.TAG) {
6
+ case "UiFragmentRegistered" :
7
+ let at = event.at;
8
+ let manifest = event.manifest;
9
+ let pluginId = event.pluginId;
10
+ return [{
11
+ TAG: "Set",
12
+ _0: pluginId,
13
+ _1: {
14
+ pluginId: pluginId,
15
+ remoteEntryUrl: manifest.remoteEntryUrl,
16
+ panels: manifest.panels,
17
+ pages: manifest.pages,
18
+ registeredAt: at,
19
+ updatedAt: at
20
+ }
21
+ }];
22
+ case "UiFragmentUpdated" :
23
+ let at$1 = event.at;
24
+ let newManifest = event.newManifest;
25
+ return [{
26
+ TAG: "Update",
27
+ _0: event.pluginId,
28
+ _1: state => ({
29
+ pluginId: state.pluginId,
30
+ remoteEntryUrl: newManifest.remoteEntryUrl,
31
+ panels: newManifest.panels,
32
+ pages: newManifest.pages,
33
+ registeredAt: state.registeredAt,
34
+ updatedAt: at$1
35
+ })
36
+ }];
37
+ case "UiFragmentDeregistered" :
38
+ return [{
39
+ TAG: "Delete",
40
+ _0: event.pluginId
41
+ }];
42
+ }
43
+ }
44
+
45
+ let Spec;
46
+
47
+ let moduleUrl = "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res.mjs";
48
+
49
+ export {
50
+ Spec,
51
+ project,
52
+ moduleUrl,
53
+ }
54
+ /* No side effect */
@@ -49,7 +49,7 @@ let sliceMutationField = (~plugin: string, ~slice: string) => `${plugin}_${slice
49
49
  let dcbCommandMutationField = (~plugin: string, ~command: string) => `${plugin}_${command}`
50
50
 
51
51
  // AppSync hard limit: subscription field names may be at most 50 chars. Each mutation
52
- // field `f` produces a `@aws_subscribe` subscription `on${f}` (see
52
+ // field `f` produces a mutation-sourced subscription `on${f}` (see
53
53
  // Plugin_SubscriptionSchema.sourceCFields), so the real cap on a mutation field is 48.
54
54
  // Fail at build with an actionable message rather than letting AppSync reject the
55
55
  // schema push at deploy time with an opaque 500.
@@ -598,15 +598,11 @@ let generate = (
598
598
  commandResultSdlTypes->Array.forEach(t => types->Array.push(t))
599
599
  }
600
600
 
601
- let encoded =
602
- JSON.Encode.object(
603
- Dict.fromArray([
604
- ("types", JSON.Encode.array(types->Array.map(JSON.Encode.string))),
605
- ("mutations", JSON.Encode.array(mutations->Array.map(JSON.Encode.string))),
606
- ("queries", JSON.Encode.array(queries->Array.map(JSON.Encode.string))),
607
- ("subscriptions", JSON.Encode.array([])),
608
- ]),
609
- )->JSON.stringify
610
-
611
- {Reventless.Plugin.encoded, protocol: "graphql"}
601
+ GraphQL_Stitcher.encode({
602
+ types,
603
+ mutations,
604
+ queries,
605
+ subscriptions: [],
606
+ subscriptionSources: [],
607
+ })
612
608
  }
@@ -5,6 +5,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
5
  import * as Stdlib_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
6
6
  import * as SchemaType$ReventlessCore from "./SchemaType.res.mjs";
7
7
  import * as StateAnnotations$Reventless from "@reventlessdev/reventless-spec/src/components/StateAnnotations.res.mjs";
8
+ import * as GraphQL_Stitcher$ReventlessCore from "./GraphQL_Stitcher.res.mjs";
8
9
 
9
10
  let commandResultSdlTypes = [
10
11
  `union CommandResult = CommandAccepted | CommandRejected | CommandPending`,
@@ -453,28 +454,13 @@ function generate(mutationEntries, queryEntries) {
453
454
  types.push(t);
454
455
  });
455
456
  }
456
- let encoded = JSON.stringify(Object.fromEntries([
457
- [
458
- "types",
459
- types.map(prim => prim)
460
- ],
461
- [
462
- "mutations",
463
- mutations.map(prim => prim)
464
- ],
465
- [
466
- "queries",
467
- queries.map(prim => prim)
468
- ],
469
- [
470
- "subscriptions",
471
- []
472
- ]
473
- ]));
474
- return {
475
- encoded: encoded,
476
- protocol: "graphql"
477
- };
457
+ return GraphQL_Stitcher$ReventlessCore.encode({
458
+ types: types,
459
+ mutations: mutations,
460
+ queries: queries,
461
+ subscriptions: [],
462
+ subscriptionSources: []
463
+ });
478
464
  }
479
465
 
480
466
  export {
@@ -0,0 +1,65 @@
1
+ // Generic, provider-neutral push planner for the event-sourced API-schema fragment registry.
2
+ //
3
+ // Given the fragments currently in the ApiFragmentRegistry (each tagged with the API it belongs to)
4
+ // it groups them by target and produces one stitched-SDL push plan per API, honouring split vs.
5
+ // unified mode. It encodes the SAME base-selection rules the deploy-time push
6
+ // (`reventless-aws Platform.preResolversSchemaHook`) applies, so the reactive single writer stays
7
+ // consistent with the deploy path:
8
+ // - split mode: the Platform API carries the admin base + Platform-target fragments; the Domain
9
+ // API carries an EMPTY base + Domain-target fragments (admin lives only on the Platform API).
10
+ // - unified mode: a single API carries the admin base + ALL fragments regardless of target.
11
+ //
12
+ // Neutral SDL only — the AWS adapter injects @aws_subscribe per plan and pushes each to the
13
+ // matching AppSync API id; the local platform (scope 2) will consume the same plans in-process.
14
+
15
+ open Reventless.Plugin
16
+
17
+ type targetedFragment = {
18
+ fragment: apiSchemaFragment,
19
+ target: apiTarget,
20
+ }
21
+
22
+ // The destination API of a push plan. In unified mode every fragment collapses onto `DomainApi`
23
+ // (the single shared API); the AWS adapter maps `PlatformApi` → the Platform AppSync id and
24
+ // `DomainApi` → the Domain AppSync id.
25
+ @schema
26
+ type planTarget = DomainApi | PlatformApi
27
+
28
+ type pushPlan = {
29
+ api: planTarget,
30
+ // Stitched neutral SDL for that API (admin/empty base + the target's fragments).
31
+ sdl: string,
32
+ }
33
+
34
+ // An empty base fragment — the Domain API's base in split mode (admin is on the Platform API).
35
+ let emptyBase: apiSchemaFragment = GraphQL_Stitcher.encode({
36
+ types: [],
37
+ queries: [],
38
+ mutations: [],
39
+ subscriptions: [],
40
+ subscriptionSources: [],
41
+ })
42
+
43
+ let planPushes = (
44
+ ~adminBase: apiSchemaFragment,
45
+ ~fragments: array<targetedFragment>,
46
+ ~splitApi: bool,
47
+ ): array<pushPlan> =>
48
+ if splitApi {
49
+ let platformFrags =
50
+ fragments->Array.filter(f => f.target == Platform)->Array.map(f => f.fragment)
51
+ let domainFrags = fragments->Array.filter(f => f.target == Domain)->Array.map(f => f.fragment)
52
+ [
53
+ {
54
+ api: PlatformApi,
55
+ sdl: GraphQL_Stitcher.stitch(~baseFragment=adminBase, ~pluginFragments=platformFrags),
56
+ },
57
+ {
58
+ api: DomainApi,
59
+ sdl: GraphQL_Stitcher.stitch(~baseFragment=emptyBase, ~pluginFragments=domainFrags),
60
+ },
61
+ ]
62
+ } else {
63
+ let allFrags = fragments->Array.map(f => f.fragment)
64
+ [{api: DomainApi, sdl: GraphQL_Stitcher.stitch(~baseFragment=adminBase, ~pluginFragments=allFrags)}]
65
+ }
@@ -0,0 +1,46 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as GraphQL_Stitcher$ReventlessCore from "./GraphQL_Stitcher.res.mjs";
5
+
6
+ let planTargetSchema = S.union([
7
+ S.literal("DomainApi"),
8
+ S.literal("PlatformApi")
9
+ ]);
10
+
11
+ let emptyBase = GraphQL_Stitcher$ReventlessCore.encode({
12
+ types: [],
13
+ mutations: [],
14
+ queries: [],
15
+ subscriptions: [],
16
+ subscriptionSources: []
17
+ });
18
+
19
+ function planPushes(adminBase, fragments, splitApi) {
20
+ if (splitApi) {
21
+ let platformFrags = fragments.filter(f => f.target === "Platform").map(f => f.fragment);
22
+ let domainFrags = fragments.filter(f => f.target === "Domain").map(f => f.fragment);
23
+ return [
24
+ {
25
+ api: "PlatformApi",
26
+ sdl: GraphQL_Stitcher$ReventlessCore.stitch(adminBase, platformFrags)
27
+ },
28
+ {
29
+ api: "DomainApi",
30
+ sdl: GraphQL_Stitcher$ReventlessCore.stitch(emptyBase, domainFrags)
31
+ }
32
+ ];
33
+ }
34
+ let allFrags = fragments.map(f => f.fragment);
35
+ return [{
36
+ api: "DomainApi",
37
+ sdl: GraphQL_Stitcher$ReventlessCore.stitch(adminBase, allFrags)
38
+ }];
39
+ }
40
+
41
+ export {
42
+ planTargetSchema,
43
+ emptyBase,
44
+ planPushes,
45
+ }
46
+ /* planTargetSchema Not a pure module */