@reventlessdev/reventless-core 3.0.0-alpha.166 → 3.0.0-alpha.167

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 (27) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +4 -4
  3. package/src/admin/AdminApi.res +9 -62
  4. package/src/admin/AdminApi.res.mjs +6 -28
  5. package/src/components/Api/GraphQL_Stitcher.res +4 -112
  6. package/src/components/Api/GraphQL_Stitcher.res.mjs +0 -90
  7. package/tests/api/GraphQL_StitcherTest.res +0 -198
  8. package/src/admin/ApiFragmentRegistry/ApiFragmentRegistryBehavior.res +0 -108
  9. package/src/admin/ApiFragmentRegistry/ApiFragmentRegistryBehavior.res.mjs +0 -190
  10. package/src/admin/ApiFragmentRegistry/ApiFragmentRegistrySpec.res +0 -71
  11. package/src/admin/ApiFragmentRegistry/ApiFragmentRegistrySpec.res.mjs +0 -92
  12. package/src/admin/ApiFragmentRegistry/ApiFragmentsProjection.res +0 -63
  13. package/src/admin/ApiFragmentRegistry/ApiFragmentsProjection.res.mjs +0 -108
  14. package/src/admin/ApiFragmentRegistry/ApiFragmentsReadModelSpec.res +0 -23
  15. package/src/admin/ApiFragmentRegistry/ApiFragmentsReadModelSpec.res.mjs +0 -43
  16. package/src/admin/Platform_ApiFragmentsApi.res +0 -32
  17. package/src/admin/Platform_ApiFragmentsApi.res.mjs +0 -52
  18. package/src/components/Api/Api_Builder.res +0 -40
  19. package/src/components/Api/Api_Builder.res.mjs +0 -33
  20. package/src/components/Api/Api_Operations.res +0 -13
  21. package/src/components/Api/Api_Operations.res.mjs +0 -12
  22. package/src/components/Api/GraphQL_PushPlanner.res +0 -65
  23. package/src/components/Api/GraphQL_PushPlanner.res.mjs +0 -46
  24. package/tests/admin/Platform_ApiFragmentsApiTest.res +0 -66
  25. package/tests/admin/Platform_ApiFragmentsApiTest.res.mjs +0 -68
  26. package/tests/api/GraphQL_PushPlannerTest.res +0 -62
  27. package/tests/api/GraphQL_PushPlannerTest.res.mjs +0 -80
@@ -1,43 +0,0 @@
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 ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
6
-
7
- let stateSchema = S.schema(s => ({
8
- pluginId: s.m(S.string),
9
- encoded: s.m(S.string),
10
- protocol: s.m(S.string),
11
- apiTarget: s.m(Plugin$Reventless.apiTargetSchema),
12
- registeredAt: s.m(S.string),
13
- updatedAt: s.m(S.string),
14
- pushStatus: s.m(S.string),
15
- pushMessage: s.m(S.string),
16
- pushedAt: s.m(S.string)
17
- }));
18
-
19
- let config = ReadModel$Reventless.config(undefined, undefined, undefined);
20
-
21
- let name = "ApiFragments";
22
-
23
- let Id;
24
-
25
- let subIdConfig;
26
-
27
- let moduleUrl = "@reventlessdev/reventless-core/src/admin/ApiFragmentRegistry/ApiFragmentsReadModelSpec.res.mjs";
28
-
29
- let authorization = "AllowAuthenticated";
30
-
31
- let visibility = "Public";
32
-
33
- export {
34
- name,
35
- Id,
36
- stateSchema,
37
- config,
38
- subIdConfig,
39
- moduleUrl,
40
- authorization,
41
- visibility,
42
- }
43
- /* stateSchema Not a pure module */
@@ -1,32 +0,0 @@
1
- // SDL type and JSON encoder for the Platform_ApiFragments GraphQL query — the
2
- // deploy-facing status surface of the API-schema fragment registry. Shared
3
- // between the in-memory adapter (which resolves from the in-process ApiFragments
4
- // read-model QueryDb) and the AWS adapter (which scans the ApiFragments
5
- // DynamoDB table), so the wire shape is byte-identical and one client query
6
- // string works against either.
7
- //
8
- // The deploy waiter polls this to confirm its plugin's fragment reached ACTIVE
9
- // (pushStatus "ok") or to surface a stitch error (pushStatus "error" +
10
- // pushMessage). It exposes status only — NOT the encoded SDL, which the waiter
11
- // does not need.
12
-
13
- let sdlTypes: array<string> = [
14
- `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}`,
15
- ]
16
-
17
- let queryFieldName: string = "Platform_ApiFragments"
18
-
19
- let sdlQueryField: string = ` ${queryFieldName}: [Platform_ApiFragmentEntry!]!`
20
-
21
- let encodeApiFragmentEntry = (entry: ApiFragmentsReadModelSpec.state): JSON.t =>
22
- Dict.fromArray([
23
- ("pluginId", JSON.Encode.string(Plugin.name(entry.pluginId))),
24
- // apiTarget is a payload-less variant — its schema serializes to the bare
25
- // JSON string "Domain" / "Platform".
26
- ("apiTarget", entry.apiTarget->S.reverseConvertToJsonOrThrow(Reventless.Plugin.apiTargetSchema)),
27
- ("pushStatus", JSON.Encode.string(entry.pushStatus)),
28
- ("pushMessage", JSON.Encode.string(entry.pushMessage)),
29
- ("pushedAt", JSON.Encode.string(entry.pushedAt)),
30
- ("registeredAt", JSON.Encode.string(entry.registeredAt)),
31
- ("updatedAt", JSON.Encode.string(entry.updatedAt)),
32
- ])->JSON.Encode.object
@@ -1,52 +0,0 @@
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 */
@@ -1,40 +0,0 @@
1
- // Api_Builder — creates the Api component backed by a Provider (AppSync / in-memory yoga).
2
- //
3
- // Usage:
4
- // module AwsApi = Api_Builder.Make(AppSync_Adapter)
5
- // let api = AwsApi.make(~name="MyApi", ~baseFragment, ~opts)
6
-
7
- module Make = (Provider: ReventlessInfra.Api_Adapter.Provider) => {
8
- let construct = (~name, ~baseFragment: Reventless.Plugin.apiSchemaFragment, self) => {
9
- let opts = {Pulumi.ComponentResource.parent: self->Component.toPulumiResource}
10
- let (api, _role) = Provider.makeApiResource(~name, ~opts)
11
-
12
- // Outputs — the cloud API ID
13
- let apiId = api->Pulumi.Output.apply(a => (a->Obj.magic)["id"]->Option.getOr(name))
14
- let _ = self->Component.setOutputs({ReventlessInfra.Api.apiId: apiId})
15
-
16
- // Operations — updateSchema rebuilds the stitched SDL and publishes it
17
- self->Component.setOperations(
18
- api->Pulumi.Output.apply(apiValue => {
19
- ReventlessInfra.Api.updateSchema: pluginFragments =>
20
- Provider.updateSchema(
21
- ~api=Pulumi.Output.make(apiValue),
22
- ~baseFragment,
23
- ~pluginFragments,
24
- ),
25
- }),
26
- )
27
- }
28
-
29
- let make = (
30
- ~name: string,
31
- ~baseFragment: Reventless.Plugin.apiSchemaFragment,
32
- ~opts: option<Pulumi.ComponentResource.options>=?,
33
- ): ReventlessInfra.Api.component =>
34
- Component.make(
35
- ~componentType="reventless:index:Api",
36
- ~name,
37
- ~construct=construct(~name, ~baseFragment, ...),
38
- ~opts,
39
- )
40
- }
@@ -1,33 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
- import * as Pulumi from "@pulumi/pulumi";
5
- import * as Component$ReventlessCore from "../Component.res.mjs";
6
-
7
- function Make(Provider) {
8
- let construct = (name, baseFragment, self) => {
9
- let opts_parent = Component$ReventlessCore.toPulumiResource(self);
10
- let opts = {
11
- parent: opts_parent
12
- };
13
- let match = Provider.makeApiResource(name, opts);
14
- let api = match[0];
15
- let apiId = api.apply(a => Stdlib_Option.getOr(a.id, name));
16
- Component$ReventlessCore.setOutputs(self, {
17
- apiId: apiId
18
- });
19
- Component$ReventlessCore.setOperations(self, api.apply(apiValue => ({
20
- updateSchema: pluginFragments => Provider.updateSchema(Pulumi.output(apiValue), baseFragment, pluginFragments)
21
- })));
22
- };
23
- let make = (name, baseFragment, opts) => Component$ReventlessCore.make("reventless:index:Api", name, extra => construct(name, baseFragment, extra), opts);
24
- return {
25
- construct: construct,
26
- make: make
27
- };
28
- }
29
-
30
- export {
31
- Make,
32
- }
33
- /* @pulumi/pulumi Not a pure module */
@@ -1,13 +0,0 @@
1
- // Api_Operations — thin runtime wrapper for Api component operations.
2
-
3
- /**
4
- Trigger a schema rebuild on the given Api component with the supplied plugin fragments.
5
- Returns a promise that resolves once the provider has published the new schema.
6
- */
7
- let updateSchema = (
8
- api: ReventlessInfra.Api.component,
9
- fragments: array<Reventless.Plugin.apiSchemaFragment>,
10
- ): Pulumi.Output.t<promise<unit>> =>
11
- api
12
- ->Component.operations
13
- ->Pulumi.Output.apply(ops => ops.updateSchema(fragments))
@@ -1,12 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Component$ReventlessCore from "../Component.res.mjs";
4
-
5
- function updateSchema(api, fragments) {
6
- return Component$ReventlessCore.operations(api).apply(ops => ops.updateSchema(fragments));
7
- }
8
-
9
- export {
10
- updateSchema,
11
- }
12
- /* Component-ReventlessCore Not a pure module */
@@ -1,65 +0,0 @@
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
- }
@@ -1,46 +0,0 @@
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 */
@@ -1,66 +0,0 @@
1
- // Asserts the canonical JSON shape produced by encodeApiFragmentEntry — the
2
- // deploy-facing status surface of the API-schema fragment registry, shared by
3
- // the in-memory and AWS adapters. Status only (no encoded SDL). If this test
4
- // changes, any deploy waiter parsing Platform_ApiFragments is affected.
5
-
6
- open JestGlobals
7
-
8
- let state: ApiFragmentsReadModelSpec.state = {
9
- pluginId: "Catalog",
10
- encoded: `{"types":[],"mutations":[],"queries":[],"subscriptions":[],"subscriptionSources":[]}`,
11
- protocol: "graphql",
12
- apiTarget: Reventless.Plugin.Domain,
13
- registeredAt: "2026-05-14T00:00:00.000Z",
14
- updatedAt: "2026-05-14T00:00:00.000Z",
15
- pushStatus: "ok",
16
- pushMessage: "",
17
- pushedAt: "2026-05-14T00:00:01.000Z",
18
- }
19
-
20
- let encoded = Platform_ApiFragmentsApi.encodeApiFragmentEntry(state)
21
- let json = encoded->JSON.stringify
22
-
23
- describe("encodeApiFragmentEntry", () => {
24
- testSync("produces a JSON object with the expected top-level keys (status only, no SDL)", () => {
25
- let dict = encoded->JSON.Decode.object->Option.getOr(Dict.make())
26
- let keys = dict->Dict.keysToArray->Array.toSorted(String.compare)
27
- expect(keys)->toEqual([
28
- "apiTarget",
29
- "pluginId",
30
- "pushMessage",
31
- "pushStatus",
32
- "pushedAt",
33
- "registeredAt",
34
- "updatedAt",
35
- ])
36
- })
37
-
38
- testSync("encodes pluginId", () =>
39
- expect(json->String.includes("\"pluginId\":\"Catalog\""))->toEqual(true)
40
- )
41
-
42
- testSync("encodes the apiTarget variant as its bare string", () =>
43
- expect(json->String.includes("\"apiTarget\":\"Domain\""))->toEqual(true)
44
- )
45
-
46
- testSync("encodes pushStatus", () =>
47
- expect(json->String.includes("\"pushStatus\":\"ok\""))->toEqual(true)
48
- )
49
-
50
- testSync("does not leak the encoded SDL into the status shape", () =>
51
- expect(json->String.includes("subscriptionSources"))->toEqual(false)
52
- )
53
- })
54
-
55
- describe("Platform_ApiFragmentsApi.sdl", () => {
56
- testSync("query field declares the list type", () =>
57
- expect(Platform_ApiFragmentsApi.sdlQueryField->String.includes("[Platform_ApiFragmentEntry!]!"))
58
- ->toEqual(true)
59
- )
60
-
61
- testSync("sdlTypes includes Platform_ApiFragmentEntry with pushStatus field", () => {
62
- let allTypes = Platform_ApiFragmentsApi.sdlTypes->Array.join("\n")
63
- expect(allTypes->String.includes("Platform_ApiFragmentEntry"))->toEqual(true)
64
- expect(allTypes->String.includes("pushStatus: String!"))->toEqual(true)
65
- })
66
- })
@@ -1,68 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
- import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
6
- import * as Platform_ApiFragmentsApi$ReventlessCore from "../../src/admin/Platform_ApiFragmentsApi.res.mjs";
7
-
8
- let state = {
9
- pluginId: "Catalog",
10
- encoded: `{"types":[],"mutations":[],"queries":[],"subscriptions":[],"subscriptionSources":[]}`,
11
- protocol: "graphql",
12
- apiTarget: "Domain",
13
- registeredAt: "2026-05-14T00:00:00.000Z",
14
- updatedAt: "2026-05-14T00:00:00.000Z",
15
- pushStatus: "ok",
16
- pushMessage: "",
17
- pushedAt: "2026-05-14T00:00:01.000Z"
18
- };
19
-
20
- let encoded = Platform_ApiFragmentsApi$ReventlessCore.encodeApiFragmentEntry(state);
21
-
22
- let json = JSON.stringify(encoded);
23
-
24
- globalThis.describe("encodeApiFragmentEntry", () => {
25
- globalThis.test("produces a JSON object with the expected top-level keys (status only, no SDL)", () => {
26
- let dict = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(encoded), {});
27
- let keys = Object.keys(dict).toSorted(Primitive_string.compare);
28
- globalThis.expect(keys).toEqual([
29
- "apiTarget",
30
- "pluginId",
31
- "pushMessage",
32
- "pushStatus",
33
- "pushedAt",
34
- "registeredAt",
35
- "updatedAt"
36
- ]);
37
- });
38
- globalThis.test("encodes pluginId", () => {
39
- globalThis.expect(json.includes("\"pluginId\":\"Catalog\"")).toEqual(true);
40
- });
41
- globalThis.test("encodes the apiTarget variant as its bare string", () => {
42
- globalThis.expect(json.includes("\"apiTarget\":\"Domain\"")).toEqual(true);
43
- });
44
- globalThis.test("encodes pushStatus", () => {
45
- globalThis.expect(json.includes("\"pushStatus\":\"ok\"")).toEqual(true);
46
- });
47
- globalThis.test("does not leak the encoded SDL into the status shape", () => {
48
- globalThis.expect(json.includes("subscriptionSources")).toEqual(false);
49
- });
50
- });
51
-
52
- globalThis.describe("Platform_ApiFragmentsApi.sdl", () => {
53
- globalThis.test("query field declares the list type", () => {
54
- globalThis.expect(Platform_ApiFragmentsApi$ReventlessCore.sdlQueryField.includes("[Platform_ApiFragmentEntry!]!")).toEqual(true);
55
- });
56
- globalThis.test("sdlTypes includes Platform_ApiFragmentEntry with pushStatus field", () => {
57
- let allTypes = Platform_ApiFragmentsApi$ReventlessCore.sdlTypes.join("\n");
58
- globalThis.expect(allTypes.includes("Platform_ApiFragmentEntry")).toEqual(true);
59
- globalThis.expect(allTypes.includes("pushStatus: String!")).toEqual(true);
60
- });
61
- });
62
-
63
- export {
64
- state,
65
- encoded,
66
- json,
67
- }
68
- /* encoded Not a pure module */
@@ -1,62 +0,0 @@
1
- open JestGlobals
2
- open GraphQL_PushPlanner
3
-
4
- // Minimal neutral fragments. `stitch` always adds Relay base types + `node`, so we assert on the
5
- // distinctive field names to know which base/fragments landed in each plan.
6
- let mkFragment = (~query: string): Reventless.Plugin.apiSchemaFragment =>
7
- GraphQL_Stitcher.encode({
8
- types: [],
9
- queries: [query],
10
- mutations: [],
11
- subscriptions: [],
12
- subscriptionSources: [],
13
- })
14
-
15
- let adminBase = mkFragment(~query=" Admin_Q: String")
16
- let domainFrag = mkFragment(~query=" Domain_Q: String")
17
- let platformFrag = mkFragment(~query=" Platform_Q: String")
18
-
19
- let fragments = [
20
- {fragment: domainFrag, target: Domain},
21
- {fragment: platformFrag, target: Platform},
22
- ]
23
-
24
- let planFor = (plans: array<pushPlan>, api: planTarget): option<pushPlan> =>
25
- plans->Array.find(p => p.api == api)
26
-
27
- describe("GraphQL_PushPlanner.planPushes", () => {
28
- testSync("split mode: admin base + Platform fragments on the Platform API only", () => {
29
- let plans = planPushes(~adminBase, ~fragments, ~splitApi=true)
30
- expect(plans->Array.length)->toBe(2)
31
- let platform = planFor(plans, PlatformApi)->Belt.Option.getExn
32
- expect(platform.sdl->String.includes("Admin_Q"))->toBe(true)
33
- expect(platform.sdl->String.includes("Platform_Q"))->toBe(true)
34
- expect(platform.sdl->String.includes("Domain_Q"))->toBe(false)
35
- })
36
-
37
- testSync("split mode: empty base + Domain fragments on the Domain API only (no admin)", () => {
38
- let plans = planPushes(~adminBase, ~fragments, ~splitApi=true)
39
- let domain = planFor(plans, DomainApi)->Belt.Option.getExn
40
- expect(domain.sdl->String.includes("Domain_Q"))->toBe(true)
41
- expect(domain.sdl->String.includes("Admin_Q"))->toBe(false)
42
- expect(domain.sdl->String.includes("Platform_Q"))->toBe(false)
43
- })
44
-
45
- testSync("unified mode: one Domain API push with admin base + ALL fragments", () => {
46
- let plans = planPushes(~adminBase, ~fragments, ~splitApi=false)
47
- expect(plans->Array.length)->toBe(1)
48
- let only = plans->Array.getUnsafe(0)
49
- expect(only.api == DomainApi)->toBe(true)
50
- expect(only.sdl->String.includes("Admin_Q"))->toBe(true)
51
- expect(only.sdl->String.includes("Domain_Q"))->toBe(true)
52
- expect(only.sdl->String.includes("Platform_Q"))->toBe(true)
53
- })
54
-
55
- testSync("empty registry still pushes the admin base (split → Platform API)", () => {
56
- let plans = planPushes(~adminBase, ~fragments=[], ~splitApi=true)
57
- let platform = planFor(plans, PlatformApi)->Belt.Option.getExn
58
- expect(platform.sdl->String.includes("Admin_Q"))->toBe(true)
59
- let domain = planFor(plans, DomainApi)->Belt.Option.getExn
60
- expect(domain.sdl->String.includes("Admin_Q"))->toBe(false)
61
- })
62
- })
@@ -1,80 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Belt_Option from "@rescript/runtime/lib/es6/Belt_Option.js";
4
- import * as GraphQL_Stitcher$ReventlessCore from "../../src/components/Api/GraphQL_Stitcher.res.mjs";
5
- import * as GraphQL_PushPlanner$ReventlessCore from "../../src/components/Api/GraphQL_PushPlanner.res.mjs";
6
-
7
- function mkFragment(query) {
8
- return GraphQL_Stitcher$ReventlessCore.encode({
9
- types: [],
10
- mutations: [],
11
- queries: [query],
12
- subscriptions: [],
13
- subscriptionSources: []
14
- });
15
- }
16
-
17
- let adminBase = mkFragment(" Admin_Q: String");
18
-
19
- let domainFrag = mkFragment(" Domain_Q: String");
20
-
21
- let platformFrag = mkFragment(" Platform_Q: String");
22
-
23
- let fragments = [
24
- {
25
- fragment: domainFrag,
26
- target: "Domain"
27
- },
28
- {
29
- fragment: platformFrag,
30
- target: "Platform"
31
- }
32
- ];
33
-
34
- function planFor(plans, api) {
35
- return plans.find(p => p.api === api);
36
- }
37
-
38
- globalThis.describe("GraphQL_PushPlanner.planPushes", () => {
39
- globalThis.test("split mode: admin base + Platform fragments on the Platform API only", () => {
40
- let plans = GraphQL_PushPlanner$ReventlessCore.planPushes(adminBase, fragments, true);
41
- globalThis.expect(plans.length).toBe(2);
42
- let platform = Belt_Option.getExn(planFor(plans, "PlatformApi"));
43
- globalThis.expect(platform.sdl.includes("Admin_Q")).toBe(true);
44
- globalThis.expect(platform.sdl.includes("Platform_Q")).toBe(true);
45
- globalThis.expect(platform.sdl.includes("Domain_Q")).toBe(false);
46
- });
47
- globalThis.test("split mode: empty base + Domain fragments on the Domain API only (no admin)", () => {
48
- let plans = GraphQL_PushPlanner$ReventlessCore.planPushes(adminBase, fragments, true);
49
- let domain = Belt_Option.getExn(planFor(plans, "DomainApi"));
50
- globalThis.expect(domain.sdl.includes("Domain_Q")).toBe(true);
51
- globalThis.expect(domain.sdl.includes("Admin_Q")).toBe(false);
52
- globalThis.expect(domain.sdl.includes("Platform_Q")).toBe(false);
53
- });
54
- globalThis.test("unified mode: one Domain API push with admin base + ALL fragments", () => {
55
- let plans = GraphQL_PushPlanner$ReventlessCore.planPushes(adminBase, fragments, false);
56
- globalThis.expect(plans.length).toBe(1);
57
- let only = plans[0];
58
- globalThis.expect(only.api === "DomainApi").toBe(true);
59
- globalThis.expect(only.sdl.includes("Admin_Q")).toBe(true);
60
- globalThis.expect(only.sdl.includes("Domain_Q")).toBe(true);
61
- globalThis.expect(only.sdl.includes("Platform_Q")).toBe(true);
62
- });
63
- globalThis.test("empty registry still pushes the admin base (split → Platform API)", () => {
64
- let plans = GraphQL_PushPlanner$ReventlessCore.planPushes(adminBase, [], true);
65
- let platform = Belt_Option.getExn(planFor(plans, "PlatformApi"));
66
- globalThis.expect(platform.sdl.includes("Admin_Q")).toBe(true);
67
- let domain = Belt_Option.getExn(planFor(plans, "DomainApi"));
68
- globalThis.expect(domain.sdl.includes("Admin_Q")).toBe(false);
69
- });
70
- });
71
-
72
- export {
73
- mkFragment,
74
- adminBase,
75
- domainFrag,
76
- platformFrag,
77
- fragments,
78
- planFor,
79
- }
80
- /* adminBase Not a pure module */