diff --git a/.github/workflows/bindings.yml b/.github/workflows/bindings.yml index 4e4e23ea..cf4107e5 100644 --- a/.github/workflows/bindings.yml +++ b/.github/workflows/bindings.yml @@ -41,3 +41,17 @@ jobs: - name: Test run: | yarn test:ci + ENABLE_PERSISTING=true yarn test:ci + yarn test:conversion + yarn test:conversion:mutations + - name: Generated conversion size budget + run: yarn size:conversion --check --output conversion-size.json + - name: Conversion benchmark (informational) + run: yarn bench:conversion:prepared --samples 7 --duration 20 --output conversion-benchmark.json + - uses: actions/upload-artifact@v4 + with: + name: conversion-performance + path: | + packages/rescript-relay/conversion-benchmark.json + packages/rescript-relay/conversion-size.json + packages/rescript-relay/coverage/conversion/ diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 0c88aac6..080f1f78 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -20,7 +20,7 @@ jobs: name: Build PPX for release runs-on: ${{ matrix.platform }} container: - image: debian:bullseye + image: debian:bookworm defaults: run: working-directory: packages/rescript-relay/rescript-relay-ppx @@ -34,6 +34,7 @@ jobs: curl \ ca-certificates \ git \ + gnupg \ unzip \ bubblewrap - uses: actions/checkout@v4 @@ -47,7 +48,7 @@ jobs: with: ocaml-compiler: "4.14" dune-cache: true - cache-prefix: "v2-bullseye" + cache-prefix: "v3-bookworm" - name: Install PPX dependencies run: opam install . --deps-only - name: Build PPX diff --git a/.github/workflows/ppx.yml b/.github/workflows/ppx.yml index 0155db8c..78f257ba 100644 --- a/.github/workflows/ppx.yml +++ b/.github/workflows/ppx.yml @@ -11,7 +11,7 @@ jobs: name: PPX Linux runs-on: ubuntu-latest container: - image: debian:bullseye + image: debian:bookworm steps: - name: Install system dependencies run: | @@ -21,6 +21,7 @@ jobs: curl \ ca-certificates \ git \ + gnupg \ unzip \ bubblewrap - uses: actions/checkout@v4 @@ -29,7 +30,7 @@ jobs: with: ocaml-compiler: "4.14" dune-cache: true - cache-prefix: "v2-bullseye" + cache-prefix: "v3-bookworm" - name: Install dependencies working-directory: packages/rescript-relay/rescript-relay-ppx run: opam install . --deps-only diff --git a/.gitignore b/.gitignore index 1beb720c..d87877ec 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,8 @@ _opam *.bs.js rescript-relay-compiler .now -.DS_Store \ No newline at end of file +.DS_Store +# Local conversion coverage and profiling output +coverage/ +*.cpuprofile +conversion-benchmark.json diff --git a/CHANGELOG.md b/CHANGELOG.md index e651bccf..e03599e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # master +- Speed up response and variable conversion with prepared conversion plans and smaller generated instructions. Fix custom-scalar, nested-list, union, and plural `@catch` conversion edge cases; expand regression coverage and add performance/bundle-size benchmarks. + - fix remove-unused-fields command. https://github.com/zth/rescript-relay/pull/636 # 4.5.1 diff --git a/build-ci.sh b/build-ci.sh index d2310d37..fcb194a0 100755 --- a/build-ci.sh +++ b/build-ci.sh @@ -26,9 +26,8 @@ cp .npmignore ../../_release/; cp ../../README.md ../../_release/; cp ../../CHANGELOG.md ../../_release/; -# copy and create mjs version of utils.js -cp src/utils.js ../../_release/src/utils.mjs; -sed -i 's/module\.exports \= /export /g' ../../_release/src/utils.mjs; +# Build matching ESM conversion modules from the CommonJS source. +node scripts/build-conversion-modules.js ../../_release/src; # copy config files echo "Copying config files..." diff --git a/packages/relay b/packages/relay index ef6d6832..24c0ec95 160000 --- a/packages/relay +++ b/packages/relay @@ -1 +1 @@ -Subproject commit ef6d68326a6661887a2a09fd12028f201e32d78f +Subproject commit 24c0ec9503478184bdd54b69dcd661650bbcb196 diff --git a/packages/rescript-relay/__tests__/Test_conversionContract-tests.js b/packages/rescript-relay/__tests__/Test_conversionContract-tests.js new file mode 100644 index 00000000..228ad40b --- /dev/null +++ b/packages/rescript-relay/__tests__/Test_conversionContract-tests.js @@ -0,0 +1,131 @@ +require("@testing-library/jest-dom/extend-expect"); +const React = require("react"); +const { render, screen, act } = require("@testing-library/react"); +const { RelayEnvironmentProvider } = require("react-relay"); +const { createMockEnvironment } = require("relay-test-utils"); +const { createOperationDescriptor } = require("relay-runtime"); +const Scalar = require("./Test_customScalars.bs"); +const Fragment = require("./Test_fragment.bs"); +const scalarNode = + require("./__generated__/TestCustomScalarsQuery_graphql.bs").node; +const fragmentNode = + require("./__generated__/TestFragmentQuery_graphql.bs").node; + +function mount(environment, Component) { + const tree = () => + React.createElement( + RelayEnvironmentProvider, + { environment }, + React.createElement( + React.Suspense, + { fallback: "Loading" }, + React.createElement(Component), + ), + ); + const view = render(tree()); + return () => view.rerender(tree()); +} + +test("compiled query converts nested scalar lists and unions after store updates without changing the store", () => { + const environment = createMockEnvironment(); + const variables = { + number: [2], + beforeDate: new Date("2018-01-01T00:00:00Z"), + }; + const wireVariables = Scalar.Query.convertVariables(variables); + expect(wireVariables).toEqual({ + number: 2, + beforeDate: "2018-01-01T00:00:00.000Z", + }); + const operation = createOperationDescriptor(scalarNode, wireVariables); + const date1 = "2020-01-01T00:00:00.000Z"; + const date2 = "2021-01-01T00:00:00.000Z"; + const payload = (date) => ({ + loggedInUser: { + id: "viewer", + createdAt: date, + friends: [{ id: "friend", createdAt: date }], + }, + member: { __typename: "User", id: "member", createdAt: date }, + }); + environment.commitPayload(operation, payload(date1)); + let converted; + function Probe() { + converted = Scalar.Query.use(variables, "store-only"); + return React.createElement( + "div", + null, + converted.loggedInUser.friends[0].createdAt.toISOString(), + ); + } + const rerender = mount(environment, Probe); + expect(screen.getByText(date1)).toBeInTheDocument(); + expect(converted.member.createdAt).toEqual(new Date(date1)); + expect( + environment.lookup(operation.fragment).data.loggedInUser.friends[0] + .createdAt, + ).toBe(date1); + const first = converted; + rerender(); + expect(converted).toBe(first); // The real binding's useMemo still applies. + act(() => environment.commitPayload(operation, payload(date2))); + expect(screen.getByText(date2)).toBeInTheDocument(); + expect(converted).not.toBe(first); + expect(converted.member.createdAt).toEqual(new Date(date2)); + expect(first.loggedInUser.friends[0].createdAt).toEqual(new Date(date1)); + expect(environment.lookup(operation.fragment).data.member.createdAt).toBe( + date2, + ); +}); + +test("compiled fragment refs can be consumed by nested and plural hooks across nullable store updates", () => { + const environment = createMockEnvironment(); + const operation = createOperationDescriptor(fragmentNode, {}); + const payload = (status) => ({ + loggedInUser: { + id: "viewer", + firstName: "Viewer", + lastName: "Last", + onlineStatus: status, + }, + users: { + edges: [ + { node: { id: "friend", firstName: "Friend", onlineStatus: status } }, + ], + }, + }); + environment.commitPayload(operation, payload("Online")); + let result; + function Probe() { + const query = Fragment.Query.use(undefined, "store-only"); + const fragment = Fragment.Fragment.use(query.loggedInUser.fragmentRefs); + const nested = Fragment.SubFragment.use(fragment.fragmentRefs); + const plural = Fragment.PluralFragment.use( + query.users.edges.map((edge) => edge.node.fragmentRefs), + ); + result = { query, fragment, nested, plural }; + return React.createElement( + "div", + null, + `${nested.lastName}:${plural[0].onlineStatus ?? "none"}`, + ); + } + mount(environment, Probe); + expect(screen.getByText("Last:Online")).toBeInTheDocument(); + expect(result.fragment.updatableFragmentRefs).toBe( + result.fragment.fragmentRefs, + ); + expect(result.query.users.edges[0].node.updatableFragmentRefs).toBe( + result.query.users.edges[0].node.fragmentRefs, + ); + act(() => environment.commitPayload(operation, payload(null))); + expect(screen.getByText("Last:none")).toBeInTheDocument(); + expect(result.fragment.onlineStatus).toBeUndefined(); + expect(result.plural[0].onlineStatus).toBeUndefined(); + // The refs still contain the wire value; only the returned data is converted. + expect(result.query.users.edges[0].node.fragmentRefs.onlineStatus).toBeNull(); + expect( + environment.lookup(operation.fragment).data.users.edges[0].node + .onlineStatus, + ).toBeNull(); +}); diff --git a/packages/rescript-relay/__tests__/Test_conversionPlan-tests.js b/packages/rescript-relay/__tests__/Test_conversionPlan-tests.js new file mode 100644 index 00000000..2ac55e74 --- /dev/null +++ b/packages/rescript-relay/__tests__/Test_conversionPlan-tests.js @@ -0,0 +1,90 @@ +require("@testing-library/jest-dom/extend-expect"); +const React = require("react"); +const { render, screen, act } = require("@testing-library/react"); +const { RelayEnvironmentProvider } = require("react-relay"); +const { createMockEnvironment } = require("relay-test-utils"); +const { createOperationDescriptor } = require("relay-runtime"); +const { Query, ReverseQuery } = require("./Test_conversionPlan.bs"); +const generated = require("./__generated__/TestConversionPlanQuery_graphql.bs"); + +const reverseGenerated = require("./__generated__/TestConversionPlanReverseQuery_graphql.bs"); + +test.each([ + ["original", Query, generated], + ["reversed", ReverseQuery, reverseGenerated], +])("%s selection order preserves scalars, nulls and nested lists through mounted hooks and writes", (_, Query, generated) => { + const environment = createMockEnvironment(); + const variables = { + input: { + grid: [[[1], undefined, [2]], undefined], + children: [{ grid: [[[3]]] }, undefined], + }, + }; + const convertedVariables = Query.convertVariables(variables); + expect(JSON.parse(JSON.stringify(convertedVariables))).toStrictEqual({ + input: { grid: [[1, null, 2], null], children: [{ grid: [[3]] }, null] }, + }); + const operation = createOperationDescriptor( + generated.node, + convertedVariables, + ); + const date = "2020-01-01T00:00:00.000Z"; + const raw = { + conversionContract: { + grid: [[1, null, 2], null], + dates: [date, null], + raw: [[[null, { x: null }], null], null], + a_b: 7, + a: { b: date }, + }, + }; + environment.commitPayload(operation, raw); + let data; + function Probe() { + data = Query.use(variables, "store-only"); + return React.createElement( + "div", + null, + data.conversionContract.a?.b?.toISOString() ?? "empty", + ); + } + render( + React.createElement( + RelayEnvironmentProvider, + { environment }, + React.createElement( + React.Suspense, + { fallback: "loading" }, + React.createElement(Probe), + ), + ), + ); + expect(screen.getByText(date)).toBeInTheDocument(); + expect(data.conversionContract).toStrictEqual({ + grid: [[[1], undefined, [2]], undefined], + dates: [new Date(date), undefined], + raw: [[[null, { x: null }], undefined], undefined], + a_b: [7], + a: { b: new Date(date) }, + }); + expect(environment.lookup(operation.fragment).data).toStrictEqual(raw); + expect(generated.Internal.convertWrapResponse(data)).toStrictEqual(raw); + act(() => + environment.commitPayload(operation, { + conversionContract: { + grid: null, + dates: null, + raw: null, + a_b: null, + a: null, + }, + }), + ); + expect(data.conversionContract).toStrictEqual({ + grid: undefined, + dates: undefined, + raw: undefined, + a_b: undefined, + a: undefined, + }); +}); diff --git a/packages/rescript-relay/__tests__/Test_conversionPlan.res b/packages/rescript-relay/__tests__/Test_conversionPlan.res new file mode 100644 index 00000000..8e8c5546 --- /dev/null +++ b/packages/rescript-relay/__tests__/Test_conversionPlan.res @@ -0,0 +1,23 @@ +module Query = %relay(` + query TestConversionPlanQuery($input: ConversionContractInput) { + conversionContract(input: $input) { + grid + dates + raw + a_b + a { b } + } + } +`) + +module ReverseQuery = %relay(` + query TestConversionPlanReverseQuery($input: ConversionContractInput) { + conversionContract(input: $input) { + a { b } + a_b + raw + dates + grid + } + } +`) diff --git a/packages/rescript-relay/__tests__/Test_conversionPlural-tests.js b/packages/rescript-relay/__tests__/Test_conversionPlural-tests.js new file mode 100644 index 00000000..127309b5 --- /dev/null +++ b/packages/rescript-relay/__tests__/Test_conversionPlural-tests.js @@ -0,0 +1,78 @@ +const React = require("react"); +const { render, act } = require("@testing-library/react"); +const { RelayEnvironmentProvider } = require("react-relay"); +const { createMockEnvironment } = require("relay-test-utils"); +const { createOperationDescriptor } = require("relay-runtime"); +const { Query, Fragment, CatchFragment, NullableFragment, UnionFragment, NodeFragment, caughtDates, nullableDates, unionValues, nodeValues } = require("./Test_conversionPlural.bs"); +const { node } = require("./__generated__/TestConversionPluralQuery_graphql.bs"); + +test("plural caught fragments convert concrete, nullable, union and interface payloads across store updates", () => { + const environment = createMockEnvironment(); + const operation = createOperationDescriptor(node, {}); + const dates = ["2020-01-01T00:00:00.000Z", "2021-01-01T00:00:00.000Z"]; + const payload = values => ({ + members: { + edges: [ + { node: { __typename: "User", id: "0", createdAt: values[0], firstName: "User 0" } }, + { node: { __typename: "Group", id: "group", name: "Group name" } }, + ], + }, + users: { + edges: values.map((createdAt, index) => ({ + node: { __typename: "User", id: String(index), createdAt, firstName: `User ${index}` }, + })), + }, + }); + environment.commitPayload(operation, payload(dates)); + let plain, caught, nullable, union, nodes; + function Probe() { + const query = Query.use(undefined, "store-only"); + const refs = query.users.edges.map(edge => edge.node.fragmentRefs); + plain = Fragment.use(refs); + caught = CatchFragment.use(refs); + nullable = NullableFragment.use(refs); + const memberRefs = query.members.edges.map(edge => edge.node.fragmentRefs); + union = UnionFragment.use(memberRefs); + nodes = NodeFragment.use(memberRefs); + return null; + } + render(React.createElement(RelayEnvironmentProvider, {environment}, React.createElement(Probe))); + const check = values => { + expect(plain.map(value => value.createdAt)).toEqual(values.map(value => new Date(value))); + expect(caught.map(value => value.ok)).toEqual(values.map(() => true)); + expect(caughtDates(caught)).toEqual(values); + expect(nullableDates(nullable)).toEqual(values); + expect(unionValues(union)).toEqual([values[0], "Group name"]); + expect(nodeValues(nodes)).toEqual([values[0], "Group name"]); + expect(values.map((_, index) => + environment.getStore().getSource().get(String(index)).createdAt, + )).toEqual(values); + }; + check(dates); + const firstPlain = plain; + const firstCaught = caught; + const updated = dates.slice().reverse(); + act(() => environment.commitPayload(operation, payload(updated))); + check(updated); + expect(plain).not.toBe(firstPlain); + expect(caught).not.toBe(firstCaught); + expect(firstPlain.map(value => value.createdAt)).toEqual(dates.map(value => new Date(value))); + expect(caughtDates(firstCaught)).toEqual(dates); + + const mixed = payload(dates); + mixed.users.edges[0].node.firstName = null; + mixed.members.edges[0].node.firstName = null; + act(() => environment.commitPayload(operation, mixed)); + expect(caught.map(value => value.ok)).toEqual([false, true]); + expect(caught[0].errors).toHaveLength(1); + expect(caughtDates(caught)).toEqual([undefined, dates[1]]); + expect(nullable.map(value => value.ok)).toEqual([true, true]); + expect(nullable[0].value).toBeUndefined(); + expect(nullableDates(nullable)).toEqual([undefined, dates[1]]); + expect(union.map(value => value.ok)).toEqual([false, true]); + expect(unionValues(union)).toEqual([undefined, "Group name"]); + expect(nodeValues(nodes)).toEqual([dates[0], "Group name"]); + expect(plain.map(value => value.createdAt)).toEqual(dates.map(value => new Date(value))); + expect(environment.getStore().getSource().get("0").firstName).toBeNull(); + expect(environment.getStore().getSource().get("1").createdAt).toBe(dates[1]); +}); diff --git a/packages/rescript-relay/__tests__/Test_conversionPlural.res b/packages/rescript-relay/__tests__/Test_conversionPlural.res new file mode 100644 index 00000000..fe16bfc2 --- /dev/null +++ b/packages/rescript-relay/__tests__/Test_conversionPlural.res @@ -0,0 +1,103 @@ +module Query = %relay(` + query TestConversionPluralQuery { + users { + edges { + node { + id + ...TestConversionPlural_user + ...TestConversionPluralCatch_user + ...TestConversionPluralNullable_user + ...TestConversionPluralNode_node + } + } + } + members(groupId: "group") { + edges { + node { + ...TestConversionPluralUnion_member + ...TestConversionPluralNode_node + } + } + } + } +`) + +module Fragment = %relay(` + fragment TestConversionPlural_user on User @relay(plural: true) { + id + createdAt + } +`) + +module CatchFragment = %relay(` + fragment TestConversionPluralCatch_user on User @relay(plural: true) @catch { + firstName @required(action: THROW) + id + createdAt + } +`) + +// Compile-time check of the plural result wrapper and its converted scalar type. +let caughtDates = (values: TestConversionPluralCatch_user_graphql.Types.fragment) => + values->Array.map(value => + switch value { + | Ok({value}) => Some(value.createdAt->Date.toISOString) + | Error(_) => None + } + ) + +module NullableFragment = %relay(` + fragment TestConversionPluralNullable_user on User @relay(plural: true) @catch { + firstName @required(action: NONE) + createdAt + } +`) + +module UnionFragment = %relay(` + fragment TestConversionPluralUnion_member on Member @relay(plural: true) @catch { + ... on User { + firstName @required(action: THROW) + createdAt + } + ... on Group { + name + } + } +`) + +module NodeFragment = %relay(` + fragment TestConversionPluralNode_node on Node @relay(plural: true) @catch { + ... on User { + createdAt + } + ... on Group { + name + } + } +`) + +let nullableDates = (values: TestConversionPluralNullable_user_graphql.Types.fragment) => + values->Array.map(value => + switch value { + | Ok({value: Some({createdAt})}) => Some(createdAt->Date.toISOString) + | _ => None + } + ) + +let unionValues = (values: TestConversionPluralUnion_member_graphql.Types.fragment) => + values->Array.map(value => + switch value { + | Ok({value: User({createdAt})}) => Some(createdAt->Date.toISOString) + | Ok({value: Group({name})}) => Some(name) + | _ => None + } + ) + +let nodeValues = (values: TestConversionPluralNode_node_graphql.Types.fragment) => + values->Array.map(value => + switch value { + | Ok({value: User({createdAt})}) => Some(createdAt->Date.toISOString) + | Ok({value: Group({name})}) => Some(name) + | _ => None + } + ) diff --git a/packages/rescript-relay/__tests__/TestsUtils.res b/packages/rescript-relay/__tests__/TestsUtils.res index a6604b59..aff75285 100644 --- a/packages/rescript-relay/__tests__/TestsUtils.res +++ b/packages/rescript-relay/__tests__/TestsUtils.res @@ -83,3 +83,17 @@ module ObjectScalar2 = { {a: "parsed2"} } } + +// An array-valued scalar distinguishes scalar arrays from GraphQL list layers. +module ConversionNumber = { + type t = array + + let parse = (value: JSON.t): t => + switch value->JSON.Decode.float { + | Some(number) => [number->Float.toInt] + | None => throw(Malformed_number) + } + + let serialize = (value: t): JSON.t => + value->Array.reduce(0, (total, item) => total + item)->JSON.Encode.int +} diff --git a/packages/rescript-relay/__tests__/__generated__/GroupAvatar_group_graphql.res b/packages/rescript-relay/__tests__/__generated__/GroupAvatar_group_graphql.res index 72663d7e..b8741586 100644 --- a/packages/rescript-relay/__tests__/__generated__/GroupAvatar_group_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/GroupAvatar_group_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/HasNameComponent_hasName_graphql.res b/packages/rescript-relay/__tests__/__generated__/HasNameComponent_hasName_graphql.res index 57a02f49..53fa011e 100644 --- a/packages/rescript-relay/__tests__/__generated__/HasNameComponent_hasName_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/HasNameComponent_hasName_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/LocalUser____relay_model_instance_graphql.res b/packages/rescript-relay/__tests__/__generated__/LocalUser____relay_model_instance_graphql.res index f9daa1d8..cd14f814 100644 --- a/packages/rescript-relay/__tests__/__generated__/LocalUser____relay_model_instance_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/LocalUser____relay_model_instance_graphql.res @@ -11,17 +11,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/LocalUser__id_graphql.res b/packages/rescript-relay/__tests__/__generated__/LocalUser__id_graphql.res index 2557adba..6d3f848a 100644 --- a/packages/rescript-relay/__tests__/__generated__/LocalUser__id_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/LocalUser__id_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/RelaySchemaAssets_graphql.res b/packages/rescript-relay/__tests__/__generated__/RelaySchemaAssets_graphql.res index fa68111d..bcfc2a73 100644 --- a/packages/rescript-relay/__tests__/__generated__/RelaySchemaAssets_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/RelaySchemaAssets_graphql.res @@ -260,3 +260,15 @@ and input_SerializeMultipleCustomScalars_nullable = { os1s: array, os2?: Null.t, } + +@live +and input_ConversionContractInput = { + grid?: array>>>, + children?: array>, +} + +@live +and input_ConversionContractInput_nullable = { + grid?: Null.t>>>>, + children?: Null.t>>, +} diff --git a/packages/rescript-relay/__tests__/__generated__/RichContent_content_graphql.res b/packages/rescript-relay/__tests__/__generated__/RichContent_content_graphql.res index 909ce99b..3026b802 100644 --- a/packages/rescript-relay/__tests__/__generated__/RichContent_content_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/RichContent_content_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestAliasedFragmentsQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragmentsQuery_graphql.res index 293316b5..66f7b35d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestAliasedFragmentsQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragmentsQuery_graphql.res @@ -34,45 +34,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.res index b10d6e18..7c4ceb21 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userLastName_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userLastName_graphql.res index fe8c1084..d1eb3b7d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userLastName_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userLastName_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.res index 762cd1a3..93c398d3 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.res @@ -39,49 +39,31 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"__root":{"beforeDate":{"c":"TestsUtils.Datetime"},"aaNumber":{"c":"TestsUtils.Number"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["aaNumber"],"scalar":"1"},{"path":["beforeDate"],"scalar":"0"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.Number": TestsUtils.Number.serialize, - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, + let variablesCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": TestsUtils.Number.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) - @live - type wrapResponseRaw - @live - let wrapResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live - type responseRaw + type wrapResponseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let responseConverterMap = () + type responseRaw @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.res index aa7641fc..24ffdd29 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.res @@ -26,49 +26,43 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_createdAt_value":{"c":"TestsUtils.Datetime"},"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]},{"path":["loggedInUser","createdAt","value"],"scalar":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, + let wrapResponseCallbacks = { + "0": TestsUtils.Datetime.serialize, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_createdAt_value":{"c":"TestsUtils.Datetime"},"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.parse, + let responseCallbacks = { + "0": TestsUtils.Datetime.parse, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.res index 94edaecf..69e503f6 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.res @@ -47,53 +47,47 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"member_User_memberOfSingular_value_User_createdAt":{"c":"TestsUtils.Datetime"},"member_User_memberOfSingular_value":{"u":"response_member_User_memberOfSingular_value"},"member":{"u":"response_member"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["member"],"union":"1"},{"path":["member","User","memberOfSingular","value"],"union":"2"},{"path":["member","User","memberOfSingular","value","User","createdAt"],"scalar":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "response_member_User_memberOfSingular_value": wrap_response_member_User_memberOfSingular_value, - "response_member": wrap_response_member, + let wrapResponseCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": wrap_response_member, + "2": wrap_response_member_User_memberOfSingular_value, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"member_User_memberOfSingular_value_User_createdAt":{"c":"TestsUtils.Datetime"},"member_User_memberOfSingular_value":{"u":"response_member_User_memberOfSingular_value"},"member":{"u":"response_member"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.parse, - "response_member_User_memberOfSingular_value": unwrap_response_member_User_memberOfSingular_value, - "response_member": unwrap_response_member, + let responseCallbacks = { + "0": TestsUtils.Datetime.parse, + "1": unwrap_response_member, + "2": unwrap_response_member_User_memberOfSingular_value, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestCatchMemberPropQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCatchMemberPropQuery_graphql.res index a9a3bc37..fe93f9c8 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCatchMemberPropQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCatchMemberPropQuery_graphql.res @@ -34,51 +34,45 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"member_value_User_createdAt":{"c":"TestsUtils.Datetime"},"member_value":{"u":"response_member_value"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["member","value"],"union":"1"},{"path":["member","value","User","createdAt"],"scalar":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "response_member_value": wrap_response_member_value, + let wrapResponseCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": wrap_response_member_value, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"member_value_User_createdAt":{"c":"TestsUtils.Datetime"},"member_value":{"u":"response_member_value"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.parse, - "response_member_value": unwrap_response_member_value, + let responseCallbacks = { + "0": TestsUtils.Datetime.parse, + "1": unwrap_response_member_value, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestCatchMembersPropQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCatchMembersPropQuery_graphql.res index ab98d67e..4722ea86 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCatchMembersPropQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCatchMembersPropQuery_graphql.res @@ -40,51 +40,45 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"members_edges_node_value_User_createdAt":{"c":"TestsUtils.Datetime"},"members_edges_node_value":{"u":"response_members_edges_node_value"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["members","edges"]},{"path":["members","edges","node","value"],"union":"1"},{"path":["members","edges","node","value","User","createdAt"],"scalar":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "response_members_edges_node_value": wrap_response_members_edges_node_value, + let wrapResponseCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": wrap_response_members_edges_node_value, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"members_edges_node_value_User_createdAt":{"c":"TestsUtils.Datetime"},"members_edges_node_value":{"u":"response_members_edges_node_value"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.parse, - "response_members_edges_node_value": unwrap_response_members_edges_node_value, + let responseCallbacks = { + "0": TestsUtils.Datetime.parse, + "1": unwrap_response_members_edges_node_value, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestCatchUnionMemberQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCatchUnionMemberQuery_graphql.res index 6414b720..f2318937 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCatchUnionMemberQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCatchUnionMemberQuery_graphql.res @@ -25,45 +25,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"member":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["member"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"member":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestCatchUnionMember_member_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCatchUnionMember_member_graphql.res index c563502a..9ca244b7 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCatchUnionMember_member_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCatchUnionMember_member_graphql.res @@ -29,21 +29,23 @@ let wrap_fragment_t: Types.fragment_t => Types.fragment_t = RescriptRelay_Intern module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"value_User_createdAt":{"c":"TestsUtils.Datetime"},"value":{"u":"fragment_t"}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["value"],"union":"1"},{"path":["value","User","createdAt"],"scalar":"0"}]},"version":2}`) @live - let fragmentConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.parse, - "fragment_t": unwrap_fragment_t, + let fragmentCallbacks = { + "0": TestsUtils.Datetime.parse, + "1": unwrap_fragment_t, } @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestCatchUser_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCatchUser_user_graphql.res index 1736e957..7fe06912 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCatchUser_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCatchUser_user_graphql.res @@ -13,20 +13,22 @@ module Types = { module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"value_createdAt":{"c":"TestsUtils.Datetime"}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["value","createdAt"],"scalar":"0"}]},"version":2}`) @live - let fragmentConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.parse, + let fragmentCallbacks = { + "0": TestsUtils.Datetime.parse, } @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestCodesplitQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCodesplitQuery_graphql.res index ae4a4053..bc2ac362 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCodesplitQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCodesplitQuery_graphql.res @@ -48,45 +48,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.res index ab1fed00..f596ef48 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestConnections2_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConnections2_user_graphql.res index 7242bffd..e0438d96 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestConnections2_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestConnections2_user_graphql.res @@ -33,20 +33,22 @@ let wrap_fragment_member: Types.fragment_member => Types.fragment_member = Rescr module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"member":{"u":"fragment_member"}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["member"],"union":"0"}]},"version":2}`) @live - let fragmentConverterMap = { - "fragment_member": unwrap_fragment_member, + let fragmentCallbacks = { + "0": unwrap_fragment_member, } @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestConnections3_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConnections3_user_graphql.res index ae2e1b96..848bab61 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestConnections3_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestConnections3_user_graphql.res @@ -25,17 +25,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestConnectionsQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConnectionsQuery_graphql.res index 2c877f79..a4bdbddb 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestConnectionsQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestConnectionsQuery_graphql.res @@ -32,48 +32,54 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"__root":{"beforeDate":{"c":"TestsUtils.Datetime"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["beforeDate"],"scalar":"0"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, + let variablesCallbacks = { + "0": TestsUtils.Datetime.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestConnections_AddFriendMutation_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConnections_AddFriendMutation_graphql.res index 8ff2787e..3df79eb4 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestConnections_AddFriendMutation_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestConnections_AddFriendMutation_graphql.res @@ -27,45 +27,15 @@ module Types = { module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestConnections_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConnections_user_graphql.res index 42cc6978..3a9f1149 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestConnections_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestConnections_user_graphql.res @@ -23,17 +23,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestConversionPlanQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConversionPlanQuery_graphql.res new file mode 100644 index 00000000..1eabf136 --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestConversionPlanQuery_graphql.res @@ -0,0 +1,261 @@ +/* @sourceLoc Test_conversionPlan.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + @live type conversionContractInput = RelaySchemaAssets_graphql.input_ConversionContractInput + type rec response_conversionContract_a = { + b: option, + } + and response_conversionContract = { + a: option, + a_b: option, + dates: option>>, + grid: option>>>>, + raw: option>>>>, + } + type response = { + conversionContract: response_conversionContract, + } + @live + type rawResponse = response + @live + type variables = { + input?: conversionContractInput, + } + @live + type refetchVariables = { + input?: option, + } + @live let makeRefetchVariables = ( + ~input=?, + ): refetchVariables => { + input: ?input + } + +} + + +type queryRef + +module Internal = { + %%private( + @live + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["input"],"reference":"conversionContractInput"}],"conversionContractInput":[{"list":1,"path":["children"],"reference":"conversionContractInput"},{"list":2,"path":["grid"],"scalar":"0"}]},"version":2}`) + @live + let variablesCallbacks = { + "0": TestsUtils.ConversionNumber.serialize, + } + @live + let preparedVariablesConverter = RescriptRelay.prepareConversion( + variablesConverter, + variablesCallbacks, + None + ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) + @live + type wrapResponseRaw + %%private( + @live + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["conversionContract","a","b"],"scalar":"1"},{"path":["conversionContract","a_b"],"scalar":"0"},{"list":1,"path":["conversionContract","dates"],"scalar":"1"},{"list":2,"path":["conversionContract","grid"],"scalar":"0"},{"list":2,"opaque":true,"path":["conversionContract","raw"]}]},"version":2}`) + @live + let wrapResponseCallbacks = { + "0": TestsUtils.ConversionNumber.serialize, + "1": TestsUtils.Datetime.serialize, + } + @live + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( + wrapResponseConverter, + wrapResponseCallbacks, + null + ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) + @live + type responseRaw + %%private( + @live + let responseConverter = wrapResponseConverter + @live + let responseCallbacks = { + "0": TestsUtils.ConversionNumber.parse, + "1": TestsUtils.Datetime.parse, + } + @live + let preparedResponseConverter = RescriptRelay.prepareConversion( + responseConverter, + responseCallbacks, + None + ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) + type wrapRawResponseRaw = wrapResponseRaw + @live + let convertWrapRawResponse = convertWrapResponse + type rawResponseRaw = responseRaw + @live + let convertRawResponse = convertResponse + type rawPreloadToken<'response> = {source: Nullable.t>} + external tokenToRaw: queryRef => rawPreloadToken = "%identity" +} +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.queryNode + + +let node: operationType = %raw(json` (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "ConversionContract", + "kind": "LinkedField", + "name": "conversionContract", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "grid", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "dates", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "raw", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "a_b", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "ConversionContractNested", + "kind": "LinkedField", + "name": "a", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "b", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ] + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "TestConversionPlanQuery", + "selections": (v1/*: any*/), + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "TestConversionPlanQuery", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "f03d25b7fdf92dbacec1faff37fd288d", + "id": null, + "metadata": {}, + "name": "TestConversionPlanQuery", + "operationKind": "query", + "text": null + } +}; +})() `) + +@live let load: ( + ~environment: RescriptRelay.Environment.t, + ~variables: Types.variables, + ~fetchPolicy: RescriptRelay.fetchPolicy=?, + ~fetchKey: string=?, + ~networkCacheConfig: RescriptRelay.cacheConfig=?, +) => queryRef = ( + ~environment, + ~variables, + ~fetchPolicy=?, + ~fetchKey=?, + ~networkCacheConfig=?, +) => + RescriptRelayReact.loadQuery( + environment, + node, + variables->Internal.convertVariables, + { + fetchKey, + fetchPolicy, + networkCacheConfig, + }, + ) + +@live +let queryRefToObservable = token => { + let raw = token->Internal.tokenToRaw + raw.source->Nullable.toOption +} + +@live +let queryRefToPromise = token => { + Promise.make((resolve, _reject) => { + switch token->queryRefToObservable { + | None => resolve(Error()) + | Some(o) => + open RescriptRelay.Observable + let _: subscription = o->subscribe(makeObserver(~complete=() => resolve(Ok()))) + } + }) +} diff --git a/packages/rescript-relay/__tests__/__generated__/TestConversionPlanReverseQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConversionPlanReverseQuery_graphql.res new file mode 100644 index 00000000..6f8804da --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestConversionPlanReverseQuery_graphql.res @@ -0,0 +1,261 @@ +/* @sourceLoc Test_conversionPlan.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + @live type conversionContractInput = RelaySchemaAssets_graphql.input_ConversionContractInput + type rec response_conversionContract_a = { + b: option, + } + and response_conversionContract = { + a: option, + a_b: option, + dates: option>>, + grid: option>>>>, + raw: option>>>>, + } + type response = { + conversionContract: response_conversionContract, + } + @live + type rawResponse = response + @live + type variables = { + input?: conversionContractInput, + } + @live + type refetchVariables = { + input?: option, + } + @live let makeRefetchVariables = ( + ~input=?, + ): refetchVariables => { + input: ?input + } + +} + + +type queryRef + +module Internal = { + %%private( + @live + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["input"],"reference":"conversionContractInput"}],"conversionContractInput":[{"list":1,"path":["children"],"reference":"conversionContractInput"},{"list":2,"path":["grid"],"scalar":"0"}]},"version":2}`) + @live + let variablesCallbacks = { + "0": TestsUtils.ConversionNumber.serialize, + } + @live + let preparedVariablesConverter = RescriptRelay.prepareConversion( + variablesConverter, + variablesCallbacks, + None + ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) + @live + type wrapResponseRaw + %%private( + @live + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["conversionContract","a","b"],"scalar":"1"},{"path":["conversionContract","a_b"],"scalar":"0"},{"list":1,"path":["conversionContract","dates"],"scalar":"1"},{"list":2,"path":["conversionContract","grid"],"scalar":"0"},{"list":2,"opaque":true,"path":["conversionContract","raw"]}]},"version":2}`) + @live + let wrapResponseCallbacks = { + "0": TestsUtils.ConversionNumber.serialize, + "1": TestsUtils.Datetime.serialize, + } + @live + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( + wrapResponseConverter, + wrapResponseCallbacks, + null + ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) + @live + type responseRaw + %%private( + @live + let responseConverter = wrapResponseConverter + @live + let responseCallbacks = { + "0": TestsUtils.ConversionNumber.parse, + "1": TestsUtils.Datetime.parse, + } + @live + let preparedResponseConverter = RescriptRelay.prepareConversion( + responseConverter, + responseCallbacks, + None + ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) + type wrapRawResponseRaw = wrapResponseRaw + @live + let convertWrapRawResponse = convertWrapResponse + type rawResponseRaw = responseRaw + @live + let convertRawResponse = convertResponse + type rawPreloadToken<'response> = {source: Nullable.t>} + external tokenToRaw: queryRef => rawPreloadToken = "%identity" +} +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.queryNode + + +let node: operationType = %raw(json` (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "ConversionContract", + "kind": "LinkedField", + "name": "conversionContract", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "ConversionContractNested", + "kind": "LinkedField", + "name": "a", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "b", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "a_b", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "raw", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "dates", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "grid", + "storageKey": null + } + ], + "storageKey": null + } + ] + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "TestConversionPlanReverseQuery", + "selections": (v1/*: any*/), + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "TestConversionPlanReverseQuery", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "b2881739785e489cd64a009ebdb584d0", + "id": null, + "metadata": {}, + "name": "TestConversionPlanReverseQuery", + "operationKind": "query", + "text": null + } +}; +})() `) + +@live let load: ( + ~environment: RescriptRelay.Environment.t, + ~variables: Types.variables, + ~fetchPolicy: RescriptRelay.fetchPolicy=?, + ~fetchKey: string=?, + ~networkCacheConfig: RescriptRelay.cacheConfig=?, +) => queryRef = ( + ~environment, + ~variables, + ~fetchPolicy=?, + ~fetchKey=?, + ~networkCacheConfig=?, +) => + RescriptRelayReact.loadQuery( + environment, + node, + variables->Internal.convertVariables, + { + fetchKey, + fetchPolicy, + networkCacheConfig, + }, + ) + +@live +let queryRefToObservable = token => { + let raw = token->Internal.tokenToRaw + raw.source->Nullable.toOption +} + +@live +let queryRefToPromise = token => { + Promise.make((resolve, _reject) => { + switch token->queryRefToObservable { + | None => resolve(Error()) + | Some(o) => + open RescriptRelay.Observable + let _: subscription = o->subscribe(makeObserver(~complete=() => resolve(Ok()))) + } + }) +} diff --git a/packages/rescript-relay/__tests__/__generated__/TestConversionPluralCatch_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralCatch_user_graphql.res new file mode 100644 index 00000000..12e0faf9 --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralCatch_user_graphql.res @@ -0,0 +1,88 @@ +/* @sourceLoc Test_conversionPlural.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + type fragment_t = { + createdAt: TestsUtils.Datetime.t, + firstName: string, + @live id: string, + } + type fragment = array> +} + +module Internal = { + @live + type fragmentRaw + %%private( + @live + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["value","createdAt"],"scalar":"0"}]},"version":2}`) + @live + let fragmentCallbacks = { + "0": TestsUtils.Datetime.parse, + } + @live + let preparedFragmentConverter = RescriptRelay.prepareConversion( + fragmentConverter, + fragmentCallbacks, + None + ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) +} + +type t +type fragmentRef +external getFragmentRef: + array | #TestConversionPluralCatch_user]>> => fragmentRef = "%identity" + +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.fragmentNode + + +let node: operationType = %raw(json` { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true, + "catchTo": "RESULT" + }, + "name": "TestConversionPluralCatch_user", + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "firstName", + "storageKey": null + }, + "action": "THROW" + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +} `) + diff --git a/packages/rescript-relay/__tests__/__generated__/TestConversionPluralNode_node_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralNode_node_graphql.res new file mode 100644 index 00000000..5d2d206d --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralNode_node_graphql.res @@ -0,0 +1,111 @@ +/* @sourceLoc Test_conversionPlural.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + @tag("__typename") type fragment_t = + | @live Group( + { + name: string, + } + ) + | @live User( + { + createdAt: TestsUtils.Datetime.t, + } + ) + | @live @as("__unselected") UnselectedUnionMember(string) + + type fragment = array> +} + +@live +let unwrap_fragment_t: Types.fragment_t => Types.fragment_t = RescriptRelay_Internal.unwrapUnion(_, ["Group", "User"]) +@live +let wrap_fragment_t: Types.fragment_t => Types.fragment_t = RescriptRelay_Internal.wrapUnion +module Internal = { + @live + type fragmentRaw + %%private( + @live + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["value"],"union":"1"},{"path":["value","User","createdAt"],"scalar":"0"}]},"version":2}`) + @live + let fragmentCallbacks = { + "0": TestsUtils.Datetime.parse, + "1": unwrap_fragment_t, + } + @live + let preparedFragmentConverter = RescriptRelay.prepareConversion( + fragmentConverter, + fragmentCallbacks, + None + ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) +} + +type t +type fragmentRef +external getFragmentRef: + array | #TestConversionPluralNode_node]>> => fragmentRef = "%identity" + +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.fragmentNode + + +let node: operationType = %raw(json` { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true, + "catchTo": "RESULT" + }, + "name": "TestConversionPluralNode_node", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "Group", + "abstractKey": null + } + ], + "type": "Node", + "abstractKey": "__isNode" +} `) + diff --git a/packages/rescript-relay/__tests__/__generated__/TestConversionPluralNullable_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralNullable_user_graphql.res new file mode 100644 index 00000000..f74e3240 --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralNullable_user_graphql.res @@ -0,0 +1,80 @@ +/* @sourceLoc Test_conversionPlural.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + type fragment_t = { + createdAt: TestsUtils.Datetime.t, + firstName: string, + } + type fragment = array>> +} + +module Internal = { + @live + type fragmentRaw + %%private( + @live + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["value","createdAt"],"scalar":"0"}]},"version":2}`) + @live + let fragmentCallbacks = { + "0": TestsUtils.Datetime.parse, + } + @live + let preparedFragmentConverter = RescriptRelay.prepareConversion( + fragmentConverter, + fragmentCallbacks, + None + ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) +} + +type t +type fragmentRef +external getFragmentRef: + array | #TestConversionPluralNullable_user]>> => fragmentRef = "%identity" + +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.fragmentNode + + +let node: operationType = %raw(json` { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true, + "catchTo": "RESULT" + }, + "name": "TestConversionPluralNullable_user", + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "firstName", + "storageKey": null + }, + "action": "NONE" + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +} `) + diff --git a/packages/rescript-relay/__tests__/__generated__/TestConversionPluralQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralQuery_graphql.res new file mode 100644 index 00000000..843d9c58 --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralQuery_graphql.res @@ -0,0 +1,412 @@ +/* @sourceLoc Test_conversionPlural.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + type rec response_members_edges_node = { + @live __typename: string, + fragmentRefs: RescriptRelay.fragmentRefs<[ | #TestConversionPluralNode_node | #TestConversionPluralUnion_member]>, + } + and response_members_edges = { + node: option, + } + and response_members = { + edges: option>>, + } + and response_users_edges_node = { + @live id: string, + fragmentRefs: RescriptRelay.fragmentRefs<[ | #TestConversionPluralCatch_user | #TestConversionPluralNode_node | #TestConversionPluralNullable_user | #TestConversionPlural_user]>, + } + and response_users_edges = { + node: option, + } + and response_users = { + edges: option>>, + } + type response = { + members: option, + users: option, + } + @live + type rawResponse = response + @live + type variables = unit + @live + type refetchVariables = unit + @live let makeRefetchVariables = () => () +} + + +type queryRef + +module Internal = { + @live + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) + @live + type wrapResponseRaw + %%private( + @live + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["members","edges"]},{"fragments":true,"path":["members","edges","node"]},{"list":1,"path":["users","edges"]},{"fragments":true,"path":["users","edges","node"]}]},"version":2}`) + @live + let wrapResponseCallbacks = () + @live + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( + wrapResponseConverter, + wrapResponseCallbacks, + null + ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) + @live + type responseRaw + %%private( + @live + let responseConverter = wrapResponseConverter + @live + let responseCallbacks = () + @live + let preparedResponseConverter = RescriptRelay.prepareConversion( + responseConverter, + responseCallbacks, + None + ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) + type wrapRawResponseRaw = wrapResponseRaw + @live + let convertWrapRawResponse = convertWrapResponse + type rawResponseRaw = responseRaw + @live + let convertRawResponse = convertResponse + type rawPreloadToken<'response> = {source: Nullable.t>} + external tokenToRaw: queryRef => rawPreloadToken = "%identity" +} +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.queryNode + + +let node: operationType = %raw(json` (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v1 = { + "args": null, + "kind": "FragmentSpread", + "name": "TestConversionPluralNode_node" +}, +v2 = [ + { + "kind": "Literal", + "name": "groupId", + "value": "group" + } +], +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v4 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null +}, +v5 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "firstName", + "storageKey": null +}, +v6 = { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "Group", + "abstractKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "TestConversionPluralQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "UserConnection", + "kind": "LinkedField", + "name": "users", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "UserEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "TestConversionPlural_user" + }, + { + "args": null, + "kind": "FragmentSpread", + "name": "TestConversionPluralCatch_user" + }, + { + "args": null, + "kind": "FragmentSpread", + "name": "TestConversionPluralNullable_user" + }, + (v1/*: any*/) + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "MemberConnection", + "kind": "LinkedField", + "name": "members", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "MemberEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "TestConversionPluralUnion_member" + }, + (v1/*: any*/) + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "members(groupId:\"group\")" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "TestConversionPluralQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "UserConnection", + "kind": "LinkedField", + "name": "users", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "UserEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + (v4/*: any*/), + (v5/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + (v3/*: any*/), + (v6/*: any*/) + ], + "type": "Node", + "abstractKey": "__isNode" + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "MemberConnection", + "kind": "LinkedField", + "name": "members", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "MemberEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "kind": "TypeDiscriminator", + "abstractKey": "__isMember" + }, + { + "kind": "InlineFragment", + "selections": [ + (v5/*: any*/), + (v4/*: any*/) + ], + "type": "User", + "abstractKey": null + }, + (v6/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + (v0/*: any*/) + ], + "type": "Node", + "abstractKey": "__isNode" + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "members(groupId:\"group\")" + } + ] + }, + "params": { + "cacheID": "12b21ea36baea2bc4e53962113b3c70f", + "id": null, + "metadata": {}, + "name": "TestConversionPluralQuery", + "operationKind": "query", + "text": "query TestConversionPluralQuery {\n users {\n edges {\n node {\n id\n ...TestConversionPlural_user\n ...TestConversionPluralCatch_user\n ...TestConversionPluralNullable_user\n ...TestConversionPluralNode_node\n }\n }\n }\n members(groupId: \"group\") {\n edges {\n node {\n __typename\n ...TestConversionPluralUnion_member\n ...TestConversionPluralNode_node\n ... on Node {\n __isNode: __typename\n __typename\n id\n }\n }\n }\n }\n}\n\nfragment TestConversionPluralCatch_user on User {\n firstName\n id\n createdAt\n}\n\nfragment TestConversionPluralNode_node on Node {\n __isNode: __typename\n __typename\n ... on User {\n createdAt\n }\n ... on Group {\n name\n }\n}\n\nfragment TestConversionPluralNullable_user on User {\n firstName\n createdAt\n}\n\nfragment TestConversionPluralUnion_member on Member {\n __isMember: __typename\n __typename\n ... on User {\n firstName\n createdAt\n }\n ... on Group {\n name\n }\n}\n\nfragment TestConversionPlural_user on User {\n id\n createdAt\n}\n" + } +}; +})() `) + +@live let load: ( + ~environment: RescriptRelay.Environment.t, + ~variables: Types.variables, + ~fetchPolicy: RescriptRelay.fetchPolicy=?, + ~fetchKey: string=?, + ~networkCacheConfig: RescriptRelay.cacheConfig=?, +) => queryRef = ( + ~environment, + ~variables, + ~fetchPolicy=?, + ~fetchKey=?, + ~networkCacheConfig=?, +) => + RescriptRelayReact.loadQuery( + environment, + node, + variables->Internal.convertVariables, + { + fetchKey, + fetchPolicy, + networkCacheConfig, + }, + ) + +@live +let queryRefToObservable = token => { + let raw = token->Internal.tokenToRaw + raw.source->Nullable.toOption +} + +@live +let queryRefToPromise = token => { + Promise.make((resolve, _reject) => { + switch token->queryRefToObservable { + | None => resolve(Error()) + | Some(o) => + open RescriptRelay.Observable + let _: subscription = o->subscribe(makeObserver(~complete=() => resolve(Ok()))) + } + }) +} diff --git a/packages/rescript-relay/__tests__/__generated__/TestConversionPluralUnion_member_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralUnion_member_graphql.res new file mode 100644 index 00000000..40c53b25 --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestConversionPluralUnion_member_graphql.res @@ -0,0 +1,123 @@ +/* @sourceLoc Test_conversionPlural.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + @tag("__typename") type fragment_t = + | @live Group( + { + name: string, + } + ) + | @live User( + { + createdAt: TestsUtils.Datetime.t, + firstName: string, + } + ) + | @live @as("__unselected") UnselectedUnionMember(string) + + type fragment = array> +} + +@live +let unwrap_fragment_t: Types.fragment_t => Types.fragment_t = RescriptRelay_Internal.unwrapUnion(_, ["Group", "User"]) +@live +let wrap_fragment_t: Types.fragment_t => Types.fragment_t = RescriptRelay_Internal.wrapUnion +module Internal = { + @live + type fragmentRaw + %%private( + @live + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["value"],"union":"1"},{"path":["value","User","createdAt"],"scalar":"0"}]},"version":2}`) + @live + let fragmentCallbacks = { + "0": TestsUtils.Datetime.parse, + "1": unwrap_fragment_t, + } + @live + let preparedFragmentConverter = RescriptRelay.prepareConversion( + fragmentConverter, + fragmentCallbacks, + None + ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) +} + +type t +type fragmentRef +external getFragmentRef: + array | #TestConversionPluralUnion_member]>> => fragmentRef = "%identity" + +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.fragmentNode + + +let node: operationType = %raw(json` { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true, + "catchTo": "RESULT" + }, + "name": "TestConversionPluralUnion_member", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "firstName", + "storageKey": null + }, + "action": "THROW" + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "Group", + "abstractKey": null + } + ], + "type": "Member", + "abstractKey": "__isMember" +} `) + diff --git a/packages/rescript-relay/__tests__/__generated__/TestConversionPlural_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestConversionPlural_user_graphql.res new file mode 100644 index 00000000..86c108ba --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestConversionPlural_user_graphql.res @@ -0,0 +1,75 @@ +/* @sourceLoc Test_conversionPlural.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + type fragment_t = { + createdAt: TestsUtils.Datetime.t, + @live id: string, + } + type fragment = array +} + +module Internal = { + @live + type fragmentRaw + %%private( + @live + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["createdAt"],"scalar":"0"}]},"version":2}`) + @live + let fragmentCallbacks = { + "0": TestsUtils.Datetime.parse, + } + @live + let preparedFragmentConverter = RescriptRelay.prepareConversion( + fragmentConverter, + fragmentCallbacks, + None + ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) +} + +type t +type fragmentRef +external getFragmentRef: + array | #TestConversionPlural_user]>> => fragmentRef = "%identity" + +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.fragmentNode + + +let node: operationType = %raw(json` { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true + }, + "name": "TestConversionPlural_user", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +} `) + diff --git a/packages/rescript-relay/__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.res index 38b1e859..5b5f885d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.res @@ -25,51 +25,45 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_intStrings":{"ca":"TestsUtils.IntString"},"loggedInUser_createdAt":{"c":"TestsUtils.Datetime"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["loggedInUser","createdAt"],"scalar":"0"},{"list":1,"path":["loggedInUser","intStrings"],"scalar":"1"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "TestsUtils.IntString": TestsUtils.IntString.serialize, + let wrapResponseCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": TestsUtils.IntString.serialize, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_intStrings":{"ca":"TestsUtils.IntString"},"loggedInUser_createdAt":{"c":"TestsUtils.Datetime"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.parse, - "TestsUtils.IntString": TestsUtils.IntString.parse, + let responseCallbacks = { + "0": TestsUtils.Datetime.parse, + "1": TestsUtils.IntString.parse, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestCustomScalarsQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestCustomScalarsQuery_graphql.res index 870857e7..2152794b 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestCustomScalarsQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestCustomScalarsQuery_graphql.res @@ -53,55 +53,61 @@ let wrap_response_member: Types.response_member => Types.response_member = Rescr type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"__root":{"number":{"c":"TestsUtils.Number"},"beforeDate":{"c":"TestsUtils.Datetime"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["beforeDate"],"scalar":"0"},{"path":["number"],"scalar":"1"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "TestsUtils.Number": TestsUtils.Number.serialize, + let variablesCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": TestsUtils.Number.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"member_User_createdAt":{"c":"TestsUtils.Datetime"},"member":{"u":"response_member"},"loggedInUser_friends_createdAt":{"c":"TestsUtils.Datetime"},"loggedInUser_createdAt":{"c":"TestsUtils.Datetime"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["loggedInUser","createdAt"],"scalar":"0"},{"list":1,"path":["loggedInUser","friends"]},{"path":["loggedInUser","friends","createdAt"],"scalar":"0"},{"path":["member"],"union":"1"},{"path":["member","User","createdAt"],"scalar":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "response_member": wrap_response_member, + let wrapResponseCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": wrap_response_member, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"member_User_createdAt":{"c":"TestsUtils.Datetime"},"member":{"u":"response_member"},"loggedInUser_friends_createdAt":{"c":"TestsUtils.Datetime"},"loggedInUser_createdAt":{"c":"TestsUtils.Datetime"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.parse, - "response_member": unwrap_response_member, + let responseCallbacks = { + "0": TestsUtils.Datetime.parse, + "1": unwrap_response_member, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestFragmentQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestFragmentQuery_graphql.res index ee9f89cc..b537547f 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestFragmentQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestFragmentQuery_graphql.res @@ -36,45 +36,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"users_edges_node":{"f":""},"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]},{"list":1,"path":["users","edges"]},{"fragments":true,"path":["users","edges","node"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"users_edges_node":{"f":""},"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.res index 126ab337..cb107790 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestFragmentRequiredQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestFragmentRequiredQuery_graphql.res index acdc53ac..27737ddc 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestFragmentRequiredQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestFragmentRequiredQuery_graphql.res @@ -35,45 +35,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"users_edges_node":{"f":""},"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]},{"list":1,"path":["users","edges"]},{"fragments":true,"path":["users","edges","node"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"users_edges_node":{"f":""},"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestFragmentRequired_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestFragmentRequired_user_graphql.res index ce4570c5..9978edac 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestFragmentRequired_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestFragmentRequired_user_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.res index e4ffbaaf..f9570c16 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestFragment_inline_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestFragment_inline_graphql.res index 80740d2b..440a4dc1 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestFragment_inline_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestFragment_inline_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestFragment_plural_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestFragment_plural_user_graphql.res index 7d1e1c22..5d399f3e 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestFragment_plural_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestFragment_plural_user_graphql.res @@ -16,17 +16,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestFragment_sub_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestFragment_sub_user_graphql.res index aff0cdd4..1580a441 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestFragment_sub_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestFragment_sub_user_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestFragment_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestFragment_user_graphql.res index d64f871c..a150a804 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestFragment_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestFragment_user_graphql.res @@ -15,18 +15,20 @@ module Types = { module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":[]}]},"version":2}`) @live - let fragmentConverterMap = () + let fragmentCallbacks = () @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestInputUnionQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestInputUnionQuery_graphql.res index 3bb7cacf..2dead8a0 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestInputUnionQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestInputUnionQuery_graphql.res @@ -32,46 +32,28 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"location":{"byLoc":{"r":"byLoc"},"byAddress":{"r":"byAddress"}},"byAddress":{},"byLoc":{},"__root":{"location":{"r":"location"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["location"],"reference":"location"}],"byAddress":[],"byLoc":[],"location":[{"path":["byAddress"],"reference":"byAddress"},{"path":["byLoc"],"reference":"byLoc"}]},"version":2}`) @live - let variablesConverterMap = () + let variablesCallbacks = () @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) - @live - type wrapResponseRaw - @live - let wrapResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live - type responseRaw + type wrapResponseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let responseConverterMap = () + type responseRaw @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestLocalPayloadQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestLocalPayloadQuery_graphql.res index 57f7d3f4..a8e639f8 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestLocalPayloadQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestLocalPayloadQuery_graphql.res @@ -93,81 +93,79 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) @live type wrapRawResponseRaw + %%private( @live - let wrapRawResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_memberOf_Group_topMember":{"u":"rawResponse_loggedInUser_memberOf_Group_topMember"},"loggedInUser_memberOfSingular":{"u":"rawResponse_loggedInUser_memberOfSingular"},"loggedInUser_memberOf":{"u":"rawResponse_loggedInUser_memberOf"}}}` - ) + let wrapRawResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["loggedInUser","memberOf"],"union":"0"},{"path":["loggedInUser","memberOf","Group","topMember"],"union":"2"},{"path":["loggedInUser","memberOfSingular"],"union":"1"}]},"version":2}`) @live - let wrapRawResponseConverterMap = { - "rawResponse_loggedInUser_memberOf_Group_topMember": wrap_rawResponse_loggedInUser_memberOf_Group_topMember, - "rawResponse_loggedInUser_memberOf": wrap_rawResponse_loggedInUser_memberOf, - "rawResponse_loggedInUser_memberOfSingular": wrap_rawResponse_loggedInUser_memberOfSingular, + let wrapRawResponseCallbacks = { + "0": wrap_rawResponse_loggedInUser_memberOf, + "1": wrap_rawResponse_loggedInUser_memberOfSingular, + "2": wrap_rawResponse_loggedInUser_memberOf_Group_topMember, } @live - let convertWrapRawResponse = v => v->RescriptRelay.convertObj( + let preparedWrapRawResponseConverter = RescriptRelay.prepareConversion( wrapRawResponseConverter, - wrapRawResponseConverterMap, + wrapRawResponseCallbacks, null ) + ) + @live + let convertWrapRawResponse = value => RescriptRelay.runConversion(preparedWrapRawResponseConverter, value) @live type rawResponseRaw + %%private( @live - let rawResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_memberOf_Group_topMember":{"u":"rawResponse_loggedInUser_memberOf_Group_topMember"},"loggedInUser_memberOfSingular":{"u":"rawResponse_loggedInUser_memberOfSingular"},"loggedInUser_memberOf":{"u":"rawResponse_loggedInUser_memberOf"}}}` - ) + let rawResponseConverter = wrapRawResponseConverter @live - let rawResponseConverterMap = { - "rawResponse_loggedInUser_memberOf_Group_topMember": unwrap_rawResponse_loggedInUser_memberOf_Group_topMember, - "rawResponse_loggedInUser_memberOf": unwrap_rawResponse_loggedInUser_memberOf, - "rawResponse_loggedInUser_memberOfSingular": unwrap_rawResponse_loggedInUser_memberOfSingular, + let rawResponseCallbacks = { + "0": unwrap_rawResponse_loggedInUser_memberOf, + "1": unwrap_rawResponse_loggedInUser_memberOfSingular, + "2": unwrap_rawResponse_loggedInUser_memberOf_Group_topMember, } @live - let convertRawResponse = v => v->RescriptRelay.convertObj( + let preparedRawResponseConverter = RescriptRelay.prepareConversion( rawResponseConverter, - rawResponseConverterMap, + rawResponseCallbacks, None ) + ) + @live + let convertRawResponse = value => RescriptRelay.runConversion(preparedRawResponseConverter, value) type rawPreloadToken<'response> = {source: Nullable.t>} external tokenToRaw: queryRef => rawPreloadToken = "%identity" } diff --git a/packages/rescript-relay/__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.res index 13d059da..0db7b9cd 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.res @@ -118,87 +118,85 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node_User":{"f":""},"node":{"u":"response_node"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["node"],"union":"0"},{"fragments":true,"path":["node","User"]}]},"version":2}`) @live - let wrapResponseConverterMap = { - "response_node": wrap_response_node, + let wrapResponseCallbacks = { + "0": wrap_response_node, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node_User":{"f":""},"node":{"u":"response_node"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "response_node": unwrap_response_node, + let responseCallbacks = { + "0": unwrap_response_node, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) @live type wrapRawResponseRaw + %%private( @live - let wrapRawResponseConverter: dict>> = %raw( - json`{"__root":{"node_User_memberOf_Group_topMember":{"u":"rawResponse_node_User_memberOf_Group_topMember"},"node_User_memberOfSingular":{"u":"rawResponse_node_User_memberOfSingular"},"node_User_memberOf":{"u":"rawResponse_node_User_memberOf"},"node":{"u":"rawResponse_node"}}}` - ) + let wrapRawResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["node"],"union":"0"},{"list":1,"path":["node","User","memberOf"],"union":"1"},{"path":["node","User","memberOf","Group","topMember"],"union":"3"},{"path":["node","User","memberOfSingular"],"union":"2"}]},"version":2}`) @live - let wrapRawResponseConverterMap = { - "rawResponse_node_User_memberOf_Group_topMember": wrap_rawResponse_node_User_memberOf_Group_topMember, - "rawResponse_node_User_memberOf": wrap_rawResponse_node_User_memberOf, - "rawResponse_node_User_memberOfSingular": wrap_rawResponse_node_User_memberOfSingular, - "rawResponse_node": wrap_rawResponse_node, + let wrapRawResponseCallbacks = { + "0": wrap_rawResponse_node, + "1": wrap_rawResponse_node_User_memberOf, + "2": wrap_rawResponse_node_User_memberOfSingular, + "3": wrap_rawResponse_node_User_memberOf_Group_topMember, } @live - let convertWrapRawResponse = v => v->RescriptRelay.convertObj( + let preparedWrapRawResponseConverter = RescriptRelay.prepareConversion( wrapRawResponseConverter, - wrapRawResponseConverterMap, + wrapRawResponseCallbacks, null ) + ) + @live + let convertWrapRawResponse = value => RescriptRelay.runConversion(preparedWrapRawResponseConverter, value) @live type rawResponseRaw + %%private( @live - let rawResponseConverter: dict>> = %raw( - json`{"__root":{"node_User_memberOf_Group_topMember":{"u":"rawResponse_node_User_memberOf_Group_topMember"},"node_User_memberOfSingular":{"u":"rawResponse_node_User_memberOfSingular"},"node_User_memberOf":{"u":"rawResponse_node_User_memberOf"},"node":{"u":"rawResponse_node"}}}` - ) + let rawResponseConverter = wrapRawResponseConverter @live - let rawResponseConverterMap = { - "rawResponse_node_User_memberOf_Group_topMember": unwrap_rawResponse_node_User_memberOf_Group_topMember, - "rawResponse_node_User_memberOf": unwrap_rawResponse_node_User_memberOf, - "rawResponse_node_User_memberOfSingular": unwrap_rawResponse_node_User_memberOfSingular, - "rawResponse_node": unwrap_rawResponse_node, + let rawResponseCallbacks = { + "0": unwrap_rawResponse_node, + "1": unwrap_rawResponse_node_User_memberOf, + "2": unwrap_rawResponse_node_User_memberOfSingular, + "3": unwrap_rawResponse_node_User_memberOf_Group_topMember, } @live - let convertRawResponse = v => v->RescriptRelay.convertObj( + let preparedRawResponseConverter = RescriptRelay.prepareConversion( rawResponseConverter, - rawResponseConverterMap, + rawResponseCallbacks, None ) + ) + @live + let convertRawResponse = value => RescriptRelay.runConversion(preparedRawResponseConverter, value) type rawPreloadToken<'response> = {source: Nullable.t>} external tokenToRaw: queryRef => rawPreloadToken = "%identity" } diff --git a/packages/rescript-relay/__tests__/__generated__/TestLocalPayload_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestLocalPayload_user_graphql.res index 41529f74..2a7a64b5 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestLocalPayload_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestLocalPayload_user_graphql.res @@ -63,22 +63,24 @@ let wrap_fragment_memberOfSingular: Types.fragment_memberOfSingular => Types.fra module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"memberOf_Group_topMember":{"u":"fragment_memberOf_Group_topMember"},"memberOfSingular":{"u":"fragment_memberOfSingular"},"memberOf":{"u":"fragment_memberOf"}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["memberOf"],"union":"0"},{"path":["memberOf","Group","topMember"],"union":"2"},{"path":["memberOfSingular"],"union":"1"}]},"version":2}`) @live - let fragmentConverterMap = { - "fragment_memberOf_Group_topMember": unwrap_fragment_memberOf_Group_topMember, - "fragment_memberOf": unwrap_fragment_memberOf, - "fragment_memberOfSingular": unwrap_fragment_memberOfSingular, + let fragmentCallbacks = { + "0": unwrap_fragment_memberOf, + "1": unwrap_fragment_memberOfSingular, + "2": unwrap_fragment_memberOf_Group_topMember, } @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.res index 23c6a7a4..b535ba4b 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.res @@ -24,45 +24,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.res index 5852b1b2..c5bf5b76 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.res @@ -33,49 +33,43 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node":{"u":"response_node"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["node"],"union":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "response_node": wrap_response_node, + let wrapResponseCallbacks = { + "0": wrap_response_node, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node":{"u":"response_node"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "response_node": unwrap_response_node, + let responseCallbacks = { + "0": unwrap_response_node, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.res index e7d1d346..80c2f639 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.res @@ -24,45 +24,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.res index 867b407b..c9b438dd 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.res @@ -24,45 +24,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.res index 94ce9bc8..23c4c688 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.res @@ -24,45 +24,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.res index 6f37c105..aedf4f5d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.res @@ -33,49 +33,43 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node":{"u":"response_node"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["node"],"union":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "response_node": wrap_response_node, + let wrapResponseCallbacks = { + "0": wrap_response_node, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node":{"u":"response_node"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "response_node": unwrap_response_node, + let responseCallbacks = { + "0": unwrap_response_node, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestMutationInline_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMutationInline_user_graphql.res index 08abfc24..f1b8f229 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMutationInline_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMutationInline_user_graphql.res @@ -16,17 +16,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestMutationProvidedVariable_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMutationProvidedVariable_user_graphql.res index deac2a73..549f3e72 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMutationProvidedVariable_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMutationProvidedVariable_user_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestMutationQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMutationQuery_graphql.res index ddd1e3da..da2bd916 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMutationQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMutationQuery_graphql.res @@ -24,45 +24,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.res index 80abd7ac..7c38ed04 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.res @@ -28,48 +28,30 @@ module Types = { } module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"recursiveSetOnlineStatusInput":{"someValue":{"c":"TestsUtils.IntString"},"setOnlineStatus":{"r":"setOnlineStatusInput"}},"setOnlineStatusInput":{"someJsonValue":{"b":""},"recursed":{"r":"recursiveSetOnlineStatusInput"}},"__root":{"input":{"r":"setOnlineStatusInput"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["input"],"reference":"setOnlineStatusInput"}],"recursiveSetOnlineStatusInput":[{"path":["setOnlineStatus"],"reference":"setOnlineStatusInput"},{"path":["someValue"],"scalar":"0"}],"setOnlineStatusInput":[{"path":["recursed"],"reference":"recursiveSetOnlineStatusInput"},{"opaque":true,"path":["someJsonValue"]}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.IntString": TestsUtils.IntString.serialize, + let variablesCallbacks = { + "0": TestsUtils.IntString.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) - @live - type wrapResponseRaw - @live - let wrapResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live - type responseRaw + type wrapResponseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let responseConverterMap = () + type responseRaw @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.res index de98797d..eadf26e5 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.res @@ -42,73 +42,47 @@ module Types = { module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"setOnlineStatus_user":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["setOnlineStatus","user"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"setOnlineStatus_user":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) - @live - type wrapRawResponseRaw - @live - let wrapRawResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapRawResponseConverterMap = () + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) @live - let convertWrapRawResponse = v => v->RescriptRelay.convertObj( - wrapRawResponseConverter, - wrapRawResponseConverterMap, - null - ) - @live - type rawResponseRaw + type wrapRawResponseRaw @live - let rawResponseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapRawResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let rawResponseConverterMap = () + type rawResponseRaw @live - let convertRawResponse = v => v->RescriptRelay.convertObj( - rawResponseConverter, - rawResponseConverterMap, - None - ) + let convertRawResponse = value => RescriptRelay.convertWithoutPlan(value, None) } module Utils = { @@warning("-33") diff --git a/packages/rescript-relay/__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.res index 63e5438e..19df643f 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.res @@ -39,73 +39,47 @@ module Types = { module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"setOnlineStatus_user":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["setOnlineStatus","user"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"setOnlineStatus_user":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) - @live - type wrapRawResponseRaw - @live - let wrapRawResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapRawResponseConverterMap = () + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) @live - let convertWrapRawResponse = v => v->RescriptRelay.convertObj( - wrapRawResponseConverter, - wrapRawResponseConverterMap, - null - ) - @live - type rawResponseRaw + type wrapRawResponseRaw @live - let rawResponseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapRawResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let rawResponseConverterMap = () + type rawResponseRaw @live - let convertRawResponse = v => v->RescriptRelay.convertObj( - rawResponseConverter, - rawResponseConverterMap, - None - ) + let convertRawResponse = value => RescriptRelay.convertWithoutPlan(value, None) } module Utils = { @@warning("-33") diff --git a/packages/rescript-relay/__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.res index 911895b6..475fa9ce 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.res @@ -61,77 +61,75 @@ let unwrap_rawResponse_setOnlineStatus_user_memberOf: Types.rawResponse_setOnlin let wrap_rawResponse_setOnlineStatus_user_memberOf: Types.rawResponse_setOnlineStatus_user_memberOf => Types.rawResponse_setOnlineStatus_user_memberOf = RescriptRelay_Internal.wrapUnion module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"setOnlineStatus_user":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["setOnlineStatus","user"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"setOnlineStatus_user":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) @live type wrapRawResponseRaw + %%private( @live - let wrapRawResponseConverter: dict>> = %raw( - json`{"__root":{"setOnlineStatus_user_memberOf":{"u":"rawResponse_setOnlineStatus_user_memberOf"}}}` - ) + let wrapRawResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["setOnlineStatus","user","memberOf"],"union":"0"}]},"version":2}`) @live - let wrapRawResponseConverterMap = { - "rawResponse_setOnlineStatus_user_memberOf": wrap_rawResponse_setOnlineStatus_user_memberOf, + let wrapRawResponseCallbacks = { + "0": wrap_rawResponse_setOnlineStatus_user_memberOf, } @live - let convertWrapRawResponse = v => v->RescriptRelay.convertObj( + let preparedWrapRawResponseConverter = RescriptRelay.prepareConversion( wrapRawResponseConverter, - wrapRawResponseConverterMap, + wrapRawResponseCallbacks, null ) + ) + @live + let convertWrapRawResponse = value => RescriptRelay.runConversion(preparedWrapRawResponseConverter, value) @live type rawResponseRaw + %%private( @live - let rawResponseConverter: dict>> = %raw( - json`{"__root":{"setOnlineStatus_user_memberOf":{"u":"rawResponse_setOnlineStatus_user_memberOf"}}}` - ) + let rawResponseConverter = wrapRawResponseConverter @live - let rawResponseConverterMap = { - "rawResponse_setOnlineStatus_user_memberOf": unwrap_rawResponse_setOnlineStatus_user_memberOf, + let rawResponseCallbacks = { + "0": unwrap_rawResponse_setOnlineStatus_user_memberOf, } @live - let convertRawResponse = v => v->RescriptRelay.convertObj( + let preparedRawResponseConverter = RescriptRelay.prepareConversion( rawResponseConverter, - rawResponseConverterMap, + rawResponseCallbacks, None ) + ) + @live + let convertRawResponse = value => RescriptRelay.runConversion(preparedRawResponseConverter, value) } module Utils = { @@warning("-33") diff --git a/packages/rescript-relay/__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.res index c35ca0cc..732590e4 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.res @@ -24,45 +24,39 @@ module Types = { module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"updateUserAvatar_user":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["updateUserAvatar","user"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"updateUserAvatar_user":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestMutation_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestMutation_user_graphql.res index 170618f3..f45fe136 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestMutation_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestMutation_user_graphql.res @@ -33,20 +33,22 @@ let wrap_fragment_memberOf: Types.fragment_memberOf => Types.fragment_memberOf = module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"memberOf":{"u":"fragment_memberOf"}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["memberOf"],"union":"0"}]},"version":2}`) @live - let fragmentConverterMap = { - "fragment_memberOf": unwrap_fragment_memberOf, + let fragmentCallbacks = { + "0": unwrap_fragment_memberOf, } @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.res index 695288ae..b9f3070d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.res @@ -38,49 +38,43 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node":{"u":"response_node"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["node"],"union":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "response_node": wrap_response_node, + let wrapResponseCallbacks = { + "0": wrap_response_node, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node":{"u":"response_node"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "response_node": unwrap_response_node, + let responseCallbacks = { + "0": unwrap_response_node, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestNodeInterfaceQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestNodeInterfaceQuery_graphql.res index f95a6428..ff70d555 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestNodeInterfaceQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestNodeInterfaceQuery_graphql.res @@ -50,51 +50,45 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node_User":{"f":""},"node":{"u":"response_node"},"_testExhaustiveInterface":{"u":"response__testExhaustiveInterface"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["_testExhaustiveInterface"],"union":"0"},{"path":["node"],"union":"1"},{"fragments":true,"path":["node","User"]}]},"version":2}`) @live - let wrapResponseConverterMap = { - "response__testExhaustiveInterface": wrap_response__testExhaustiveInterface, - "response_node": wrap_response_node, + let wrapResponseCallbacks = { + "0": wrap_response__testExhaustiveInterface, + "1": wrap_response_node, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node_User":{"f":""},"node":{"u":"response_node"},"_testExhaustiveInterface":{"u":"response__testExhaustiveInterface"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "response__testExhaustiveInterface": unwrap_response__testExhaustiveInterface, - "response_node": unwrap_response_node, + let responseCallbacks = { + "0": unwrap_response__testExhaustiveInterface, + "1": unwrap_response_node, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestNodeInterface_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestNodeInterface_user_graphql.res index ac772e6b..1f28a326 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestNodeInterface_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestNodeInterface_user_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestNonReactQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestNonReactQuery_graphql.res index 65f42ae0..aa574124 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestNonReactQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestNonReactQuery_graphql.res @@ -25,45 +25,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestNonReact_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestNonReact_user_graphql.res index d9b6b7c4..eb9f1b2d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestNonReact_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestNonReact_user_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.res index 5cf42bc4..eaff0a20 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.res @@ -29,49 +29,43 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_intStrings":{"ca":"TestsUtils.IntString"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["loggedInUser","intStrings"],"scalar":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "TestsUtils.IntString": TestsUtils.IntString.serialize, + let wrapResponseCallbacks = { + "0": TestsUtils.IntString.serialize, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_intStrings":{"ca":"TestsUtils.IntString"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "TestsUtils.IntString": TestsUtils.IntString.parse, + let responseCallbacks = { + "0": TestsUtils.IntString.parse, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestNullableVariablesMutation_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestNullableVariablesMutation_graphql.res index 5dee5a98..398519a3 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestNullableVariablesMutation_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestNullableVariablesMutation_graphql.res @@ -28,48 +28,30 @@ module Types = { } module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"someInput":{"recursive":{"r":"someInput"},"datetime":{"c":"TestsUtils.Datetime"}},"__root":{"someInput":{"r":"someInput"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["someInput"],"reference":"someInput"}],"someInput":[{"path":["datetime"],"scalar":"0"},{"path":["recursive"],"reference":"someInput"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, + let variablesCallbacks = { + "0": TestsUtils.Datetime.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, null ) - @live - type wrapResponseRaw - @live - let wrapResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live - type responseRaw + type wrapResponseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let responseConverterMap = () + type responseRaw @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestNullableVariablesQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestNullableVariablesQuery_graphql.res index 9e4dcc3d..0f679c05 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestNullableVariablesQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestNullableVariablesQuery_graphql.res @@ -24,45 +24,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestObserverQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestObserverQuery_graphql.res index d32676d8..f24f9db7 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestObserverQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestObserverQuery_graphql.res @@ -24,45 +24,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestPaginationInNodeQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPaginationInNodeQuery_graphql.res index 75f623f6..187aa528 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPaginationInNodeQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPaginationInNodeQuery_graphql.res @@ -35,45 +35,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["node"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.res index faac0330..bdf6e0f8 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.res @@ -48,45 +48,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["node"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestPaginationInNode_query_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPaginationInNode_query_graphql.res index 122105d0..3b4b019f 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPaginationInNode_query_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPaginationInNode_query_graphql.res @@ -23,18 +23,20 @@ module Types = { module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"friendsConnection_edges_node":{"f":""}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["friendsConnection","edges"]},{"fragments":true,"path":["friendsConnection","edges","node"]}]},"version":2}`) @live - let fragmentConverterMap = () + let fragmentCallbacks = () @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestPaginationInNode_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPaginationInNode_user_graphql.res index d24b57e8..69bcca99 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPaginationInNode_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPaginationInNode_user_graphql.res @@ -18,17 +18,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestPaginationUnionQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPaginationUnionQuery_graphql.res index b07dbef4..20069298 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPaginationUnionQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPaginationUnionQuery_graphql.res @@ -30,45 +30,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":[]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.res index a7438813..3034b9ad 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.res @@ -43,45 +43,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":[]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestPaginationUnion_query_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPaginationUnion_query_graphql.res index 3044ef3f..7fdf2e3f 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPaginationUnion_query_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPaginationUnion_query_graphql.res @@ -48,20 +48,22 @@ let wrap_fragment_members_edges_node: Types.fragment_members_edges_node => Types module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"members_edges_node_User":{"f":""},"members_edges_node":{"u":"fragment_members_edges_node"}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["members","edges"]},{"path":["members","edges","node"],"union":"0"},{"fragments":true,"path":["members","edges","node","User"]}]},"version":2}`) @live - let fragmentConverterMap = { - "fragment_members_edges_node": unwrap_fragment_members_edges_node, + let fragmentCallbacks = { + "0": unwrap_fragment_members_edges_node, } @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestPaginationUnion_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPaginationUnion_user_graphql.res index 881697e7..3650655a 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPaginationUnion_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPaginationUnion_user_graphql.res @@ -17,17 +17,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.res index cc39ff17..371aa62d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.res @@ -27,49 +27,43 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_intStrings":{"ca":"TestsUtils.IntString"},"loggedInUser_intString":{"c":"TestsUtils.IntString"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["loggedInUser","intString"],"scalar":"0"},{"list":1,"path":["loggedInUser","intStrings"],"scalar":"0"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "TestsUtils.IntString": TestsUtils.IntString.serialize, + let wrapResponseCallbacks = { + "0": TestsUtils.IntString.serialize, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser_intStrings":{"ca":"TestsUtils.IntString"},"loggedInUser_intString":{"c":"TestsUtils.IntString"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "TestsUtils.IntString": TestsUtils.IntString.parse, + let responseCallbacks = { + "0": TestsUtils.IntString.parse, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.res index f1ac5687..19e0b42e 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.res @@ -24,45 +24,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.res index 53215b62..76689d24 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.res @@ -44,45 +44,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["node"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.res index ceb5e787..55d72e73 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.res @@ -19,17 +19,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePagination_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePagination_user_graphql.res index 9474ac3f..8856681d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePagination_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestPrefetchablePagination_user_graphql.res @@ -24,18 +24,20 @@ module Types = { module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"friendsConnection_edges":{"f":""}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"list":1,"path":["friendsConnection","edges"]}]},"version":2}`) @live - let fragmentConverterMap = () + let fragmentCallbacks = () @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestProvidedVariablesQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestProvidedVariablesQuery_graphql.res index d57b30ba..e2cba6d2 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestProvidedVariablesQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestProvidedVariablesQuery_graphql.res @@ -24,48 +24,54 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"inputC":{"recursiveC":{"r":"inputC"},"intStr":{"c":"TestsUtils.IntString"}},"__root":{"__relay_internal__pv__ProvidedVariablesIntStrArr":{"ca":"TestsUtils.IntString"},"__relay_internal__pv__ProvidedVariablesIntStr":{"c":"TestsUtils.IntString"},"__relay_internal__pv__ProvidedVariablesInputCArr":{"r":"inputC"},"__relay_internal__pv__ProvidedVariablesInputC":{"r":"inputC"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["__relay_internal__pv__ProvidedVariablesInputC"],"reference":"inputC"},{"list":1,"path":["__relay_internal__pv__ProvidedVariablesInputCArr"],"reference":"inputC"},{"path":["__relay_internal__pv__ProvidedVariablesIntStr"],"scalar":"0"},{"list":1,"path":["__relay_internal__pv__ProvidedVariablesIntStrArr"],"scalar":"0"}],"inputC":[{"path":["intStr"],"scalar":"0"},{"path":["recursiveC"],"reference":"inputC"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.IntString": TestsUtils.IntString.serialize, + let variablesCallbacks = { + "0": TestsUtils.IntString.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestProvidedVariables_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestProvidedVariables_user_graphql.res index ce709211..24e6d80f 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestProvidedVariables_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestProvidedVariables_user_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestQueryInputObjectsQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestQueryInputObjectsQuery_graphql.res index 96258848..537772f8 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestQueryInputObjectsQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestQueryInputObjectsQuery_graphql.res @@ -30,46 +30,28 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"searchInput":{},"__root":{"input":{"r":"searchInput"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["input"],"reference":"searchInput"}],"searchInput":[]},"version":2}`) @live - let variablesConverterMap = () + let variablesCallbacks = () @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) - @live - type wrapResponseRaw - @live - let wrapResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live - type responseRaw + type wrapResponseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let responseConverterMap = () + type responseRaw @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestQuery_graphql.res index 3b8db4ff..e97fdd48 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestQuery_graphql.res @@ -41,45 +41,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNodeQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNodeQuery_graphql.res index 0b5e84fa..d2bcbfd1 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNodeQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNodeQuery_graphql.res @@ -46,49 +46,43 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node_User":{"f":""},"node":{"u":"response_node"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["node"],"union":"0"},{"fragments":true,"path":["node","User"]}]},"version":2}`) @live - let wrapResponseConverterMap = { - "response_node": wrap_response_node, + let wrapResponseCallbacks = { + "0": wrap_response_node, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node_User":{"f":""},"node":{"u":"response_node"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "response_node": unwrap_response_node, + let responseCallbacks = { + "0": unwrap_response_node, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.res index 67f6935b..0fb39398 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.res @@ -44,45 +44,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["node"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNode_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNode_user_graphql.res index 662ed47b..0064817d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNode_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingInNode_user_graphql.res @@ -19,17 +19,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.res index dbc361d5..68013b4c 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.res @@ -22,45 +22,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":[]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingNoArgs_query_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingNoArgs_query_graphql.res index e45518d3..de972419 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingNoArgs_query_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingNoArgs_query_graphql.res @@ -16,17 +16,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingQuery_graphql.res index 0abff77c..1408dd19 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingQuery_graphql.res @@ -40,49 +40,55 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"__root":{"number":{"c":"TestsUtils.Number"},"beforeDate":{"c":"TestsUtils.Datetime"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["beforeDate"],"scalar":"0"},{"path":["number"],"scalar":"1"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "TestsUtils.Number": TestsUtils.Number.serialize, + let variablesCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": TestsUtils.Number.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.res index 9ef6c472..c3b67e42 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.res @@ -22,45 +22,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":[]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.res index a39e76d1..9c5496df 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.res @@ -16,17 +16,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordQuery_graphql.res index d67bd471..9f67dc0e 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordQuery_graphql.res @@ -40,49 +40,55 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"__root":{"number":{"c":"TestsUtils.Number"},"beforeDate":{"c":"TestsUtils.Datetime"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["beforeDate"],"scalar":"0"},{"path":["number"],"scalar":"1"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "TestsUtils.Number": TestsUtils.Number.serialize, + let variablesCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": TestsUtils.Number.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.res index 65ba518f..2f89987c 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.res @@ -51,49 +51,55 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"__root":{"number":{"c":"TestsUtils.Number"},"beforeDate":{"c":"TestsUtils.Datetime"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["beforeDate"],"scalar":"0"},{"path":["number"],"scalar":"1"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "TestsUtils.Number": TestsUtils.Number.serialize, + let variablesCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": TestsUtils.Number.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["node"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecord_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecord_user_graphql.res index 09bb452a..ac906753 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecord_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRecord_user_graphql.res @@ -23,17 +23,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRefetchQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRefetchQuery_graphql.res index f146b217..29914aa7 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetchingRefetchQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetchingRefetchQuery_graphql.res @@ -51,49 +51,55 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"__root":{"number":{"c":"TestsUtils.Number"},"beforeDate":{"c":"TestsUtils.Datetime"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["beforeDate"],"scalar":"0"},{"path":["number"],"scalar":"1"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.Datetime": TestsUtils.Datetime.serialize, - "TestsUtils.Number": TestsUtils.Number.serialize, + let variablesCallbacks = { + "0": TestsUtils.Datetime.serialize, + "1": TestsUtils.Number.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["node"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"node":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRefetching_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRefetching_user_graphql.res index a4c72777..2c2b75eb 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRefetching_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRefetching_user_graphql.res @@ -23,17 +23,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestRelayResolversAllQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRelayResolversAllQuery_graphql.res index 7c54e405..43381f60 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRelayResolversAllQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRelayResolversAllQuery_graphql.res @@ -30,45 +30,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRelayResolversQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRelayResolversQuery_graphql.res index c1438373..61a9daac 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRelayResolversQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRelayResolversQuery_graphql.res @@ -24,45 +24,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestRelayResolvers_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRelayResolvers_user_graphql.res index d3b846b2..b099c656 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRelayResolvers_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRelayResolvers_user_graphql.res @@ -15,17 +15,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver2_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver2_graphql.res index 7f8ed039..14e22519 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver2_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver2_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver_graphql.res index f3bc8ae0..33b0f4d8 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRelayUserResolver_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.res index 21658bf1..bc71c10d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.res @@ -24,45 +24,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.res index ea015ec6..418152cc 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.res @@ -21,48 +21,30 @@ module Types = { } module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"__root":{"input":{"ca":"TestsUtils.IntString"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["input"],"scalar":"0"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.IntString": TestsUtils.IntString.serialize, + let variablesCallbacks = { + "0": TestsUtils.IntString.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) - @live - type wrapResponseRaw - @live - let wrapResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live - type responseRaw + type wrapResponseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let responseConverterMap = () + type responseRaw @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.res index a1bd34b8..f583b6c1 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.res @@ -18,49 +18,31 @@ module Types = { } module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"serializeMultipleCustomScalars":{"os2":{"c":"TestsUtils.ObjectScalar2"},"os1s":{"ca":"TestsUtils.ObjectScalar1"}},"__root":{"input":{"r":"serializeMultipleCustomScalars"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["input"],"reference":"serializeMultipleCustomScalars"}],"serializeMultipleCustomScalars":[{"list":1,"path":["os1s"],"scalar":"0"},{"path":["os2"],"scalar":"1"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.ObjectScalar1": TestsUtils.ObjectScalar1.serialize, - "TestsUtils.ObjectScalar2": TestsUtils.ObjectScalar2.serialize, + let variablesCallbacks = { + "0": TestsUtils.ObjectScalar1.serialize, + "1": TestsUtils.ObjectScalar2.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) - @live - type wrapResponseRaw - @live - let wrapResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live - type responseRaw + type wrapResponseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let responseConverterMap = () + type responseRaw @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.res index 1b1295be..8d545503 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestSubscriptionQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestSubscriptionQuery_graphql.res index d3d7ce89..219a0880 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestSubscriptionQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestSubscriptionQuery_graphql.res @@ -24,45 +24,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.res index eb35c38e..7e265ecc 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.res @@ -28,31 +28,23 @@ module Types = { module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"userUpdated_user":{"f":""}}}` - ) + let responseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["userUpdated","user"]}]},"version":2}`) @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type rawResponseRaw = responseRaw @live let convertRawResponse = convertResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.res index 0cb6416b..b0e9394b 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.res @@ -26,31 +26,23 @@ module Types = { module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"userUpdated_user":{"f":""}}}` - ) + let responseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["userUpdated","user"]}]},"version":2}`) @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type rawResponseRaw = responseRaw @live let convertRawResponse = convertResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestSubscription_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestSubscription_user_graphql.res index 31f8a716..92891735 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestSubscription_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestSubscription_user_graphql.res @@ -16,17 +16,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestTestingHelpersQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestTestingHelpersQuery_graphql.res index 3ca7ef4f..458fff67 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestTestingHelpersQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestTestingHelpersQuery_graphql.res @@ -37,73 +37,47 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) - @live - type wrapRawResponseRaw - @live - let wrapRawResponseConverter: dict>> = %raw( - json`{}` ) @live - let wrapRawResponseConverterMap = () + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) @live - let convertWrapRawResponse = v => v->RescriptRelay.convertObj( - wrapRawResponseConverter, - wrapRawResponseConverterMap, - null - ) - @live - type rawResponseRaw + type wrapRawResponseRaw @live - let rawResponseConverter: dict>> = %raw( - json`{}` - ) + let convertWrapRawResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live - let rawResponseConverterMap = () + type rawResponseRaw @live - let convertRawResponse = v => v->RescriptRelay.convertObj( - rawResponseConverter, - rawResponseConverterMap, - None - ) + let convertRawResponse = value => RescriptRelay.convertWithoutPlan(value, None) type rawPreloadToken<'response> = {source: Nullable.t>} external tokenToRaw: queryRef => rawPreloadToken = "%identity" } diff --git a/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_plural_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_plural_user_graphql.res index fdd86aff..22f3ff7d 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_plural_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_plural_user_graphql.res @@ -16,17 +16,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_sub_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_sub_user_graphql.res index 39a429c5..7e77e73b 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_sub_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_sub_user_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_user_graphql.res index 62f877c5..8abe5576 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestTestingHelpers_user_graphql.res @@ -15,18 +15,20 @@ module Types = { module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":[]}]},"version":2}`) @live - let fragmentConverterMap = () + let fragmentCallbacks = () @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestUnionFragmentQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestUnionFragmentQuery_graphql.res index 81cf6d51..1f21e8f4 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestUnionFragmentQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestUnionFragmentQuery_graphql.res @@ -25,45 +25,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"member":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["member"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"member":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestUnionFragmentUser_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestUnionFragmentUser_user_graphql.res index 977fec2d..30835912 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestUnionFragmentUser_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestUnionFragmentUser_user_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestUnionFragment_member_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestUnionFragment_member_graphql.res index 4e402be6..9a6396ef 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestUnionFragment_member_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestUnionFragment_member_graphql.res @@ -28,20 +28,22 @@ let wrap_fragment: Types.fragment => Types.fragment = RescriptRelay_Internal.wra module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"User":{"f":""},"":{"u":"fragment"}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":[],"union":"0"},{"fragments":true,"path":["User"]}]},"version":2}`) @live - let fragmentConverterMap = { - "fragment": unwrap_fragment, + let fragmentCallbacks = { + "0": unwrap_fragment, } @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestUnionFragment_plural_member_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestUnionFragment_plural_member_graphql.res index 71088d52..855323b2 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestUnionFragment_plural_member_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestUnionFragment_plural_member_graphql.res @@ -29,20 +29,22 @@ let wrap_fragment: Types.fragment => Types.fragment = RescriptRelay_Internal.wra module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"User":{"f":""},"":{"u":"fragment"}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":[],"union":"0"},{"fragments":true,"path":["User"]}]},"version":2}`) @live - let fragmentConverterMap = { - "fragment": unwrap_fragment, + let fragmentCallbacks = { + "0": unwrap_fragment, } @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestUnionsQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestUnionsQuery_graphql.res index f0a2e9e0..64314359 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestUnionsQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestUnionsQuery_graphql.res @@ -70,51 +70,45 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"members_edges_node_Group_members":{"u":"response_members_edges_node_Group_members"},"members_edges_node":{"u":"response_members_edges_node"}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"list":1,"path":["members","edges"]},{"path":["members","edges","node"],"union":"0"},{"list":1,"path":["members","edges","node","Group","members"],"union":"1"}]},"version":2}`) @live - let wrapResponseConverterMap = { - "response_members_edges_node_Group_members": wrap_response_members_edges_node_Group_members, - "response_members_edges_node": wrap_response_members_edges_node, + let wrapResponseCallbacks = { + "0": wrap_response_members_edges_node, + "1": wrap_response_members_edges_node_Group_members, } @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"members_edges_node_Group_members":{"u":"response_members_edges_node_Group_members"},"members_edges_node":{"u":"response_members_edges_node"}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = { - "response_members_edges_node_Group_members": unwrap_response_members_edges_node_Group_members, - "response_members_edges_node": unwrap_response_members_edges_node, + let responseCallbacks = { + "0": unwrap_response_members_edges_node, + "1": unwrap_response_members_edges_node_Group_members, } @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.res index 4c937703..00e23b3f 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.res @@ -25,45 +25,39 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":[]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/TestUpdatableFragments_query_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestUpdatableFragments_query_graphql.res index bc7c8a40..e01cebf0 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestUpdatableFragments_query_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestUpdatableFragments_query_graphql.res @@ -21,18 +21,20 @@ module Types = { module Internal = { @live type fragmentRaw + %%private( @live - let fragmentConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let fragmentConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let fragmentConverterMap = () + let fragmentCallbacks = () @live - let convertFragment = v => v->RescriptRelay.convertObj( + let preparedFragmentConverter = RescriptRelay.prepareConversion( fragmentConverter, - fragmentConverterMap, + fragmentCallbacks, None ) + ) + @live + let convertFragment = value => RescriptRelay.runConversion(preparedFragmentConverter, value) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.res index 49977a37..4db054c8 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.res @@ -26,17 +26,7 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) } diff --git a/packages/rescript-relay/__tests__/__generated__/TestUpdatableQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestUpdatableQuery_graphql.res index da75e9d3..c0f2a4c8 100644 --- a/packages/rescript-relay/__tests__/__generated__/TestUpdatableQuery_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/TestUpdatableQuery_graphql.res @@ -38,45 +38,15 @@ type queryRef module Internal = { @live - let variablesConverter: dict>> = %raw( - json`{}` - ) - @live - let variablesConverterMap = () - @live - let convertVariables = v => v->RescriptRelay.convertObj( - variablesConverter, - variablesConverterMap, - None - ) + let convertVariables = value => RescriptRelay.convertWithoutPlan(value, None) @live type wrapResponseRaw @live - let wrapResponseConverter: dict>> = %raw( - json`{}` - ) - @live - let wrapResponseConverterMap = () - @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( - wrapResponseConverter, - wrapResponseConverterMap, - null - ) + let convertWrapResponse = value => RescriptRelay.convertWithoutPlan(value, null) @live type responseRaw @live - let responseConverter: dict>> = %raw( - json`{}` - ) - @live - let responseConverterMap = () - @live - let convertResponse = v => v->RescriptRelay.convertObj( - responseConverter, - responseConverterMap, - None - ) + let convertResponse = value => RescriptRelay.convertWithoutPlan(value, None) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests__/__generated__/UserAvatar_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/UserAvatar_user_graphql.res index e8a9be0d..f3ce1402 100644 --- a/packages/rescript-relay/__tests__/__generated__/UserAvatar_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/UserAvatar_user_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/UserMeta____relay_model_instance_graphql.res b/packages/rescript-relay/__tests__/__generated__/UserMeta____relay_model_instance_graphql.res index 41ee84c2..247c96da 100644 --- a/packages/rescript-relay/__tests__/__generated__/UserMeta____relay_model_instance_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/UserMeta____relay_model_instance_graphql.res @@ -11,17 +11,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/__generated__/UserName_user_graphql.res b/packages/rescript-relay/__tests__/__generated__/UserName_user_graphql.res index 760a1274..6780204d 100644 --- a/packages/rescript-relay/__tests__/__generated__/UserName_user_graphql.res +++ b/packages/rescript-relay/__tests__/__generated__/UserName_user_graphql.res @@ -14,17 +14,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests__/helpers/conversionModel.js b/packages/rescript-relay/__tests__/helpers/conversionModel.js new file mode 100644 index 00000000..9a5f504b --- /dev/null +++ b/packages/rescript-relay/__tests__/helpers/conversionModel.js @@ -0,0 +1,139 @@ +// Independent, intentionally allocating evaluator over a schema-shaped model. +// It does not use the runtime's path tree, compilation, or copy-on-write logic. +const object = (fields = {}, fragments = false) => ({ + kind: "object", + fields, + fragments, +}); +const list = (item) => ({ kind: "list", item }); +const scalar = (name) => ({ kind: "scalar", name }); +const opaque = { kind: "opaque" }; +const reference = (name) => ({ kind: "reference", name }); +const union = (name, members) => ({ kind: "union", name, members }); +const isMetadata = (key) => + key.startsWith("__") && + key !== "__id" && + key !== "__typename" && + !key.startsWith("__relay_internal"); +const isOption = (v) => + v != null && typeof v === "object" && v.BS_PRIVATE_NESTED_SOME_NONE >= 0; +function unwrap(v) { + return v && Object.hasOwn(v, "__$inputUnion") + ? { [v.__$inputUnion]: v._0 } + : v; +} +function evaluate(schema, value, definitions, converters, nullable) { + if (value == null) return nullable; + if (isOption(value)) return value; + switch (schema.kind) { + case "scalar": + return converters[schema.name](value); + case "opaque": + return value; + case "list": + return value.map((v) => + evaluate(schema.item, v, definitions, converters, nullable), + ); + case "reference": + return evaluate( + definitions[schema.name], + unwrap(value), + definitions, + converters, + nullable, + ); + case "union": { + const member = schema.members[value.__typename] || object(); + const raw = nullable === null ? converters[schema.name](value) : value; + const converted = evaluate( + member, + raw, + definitions, + converters, + nullable, + ); + return nullable === undefined + ? converters[schema.name](converted) + : converted; + } + case "object": { + if (Array.isArray(value)) + return value.map((v) => + evaluate(schema, v, definitions, converters, nullable), + ); + const result = { ...value }; + for (const key of Object.keys(value)) { + if (isMetadata(key)) continue; + const field = schema.fields[key]; + result[key] = field + ? evaluate(field, value[key], definitions, converters, nullable) + : normalize(value[key], nullable); + } + if (schema.fragments) { + result.fragmentRefs = { ...value }; + result.updatableFragmentRefs = result.fragmentRefs; + } + return result; + } + default: + throw new Error("Invalid model kind"); + } +} +function normalize(v, nullable) { + if (v == null) return nullable; + if (isOption(v) || typeof v !== "object") return v; + if (Array.isArray(v)) return v.map((item) => normalize(item, nullable)); + return Object.fromEntries( + Object.keys(v).map((key) => [ + key, + isMetadata(key) ? v[key] : normalize(v[key], nullable), + ]), + ); +} +function encode(definitions) { + const roots = {}; + for (const [name, schema] of Object.entries(definitions)) { + const entries = []; + function visit(node, path) { + const entry = { path }; + let depth = 0; + while (node.kind === "list") { + depth++; + node = node.item; + } + if (depth) entry.list = depth; + if (node.kind === "scalar") entry.scalar = node.name; + if (node.kind === "reference") entry.reference = node.name; + if (node.kind === "opaque") entry.opaque = true; + if (node.kind === "union") entry.union = node.name; + if (node.kind === "object" && node.fragments) entry.fragments = true; + if (Object.keys(entry).length > 1) entries.push(entry); + if (node.kind === "object") + for (const [key, child] of Object.entries(node.fields)) + visit(child, [...path, key]); + if (node.kind === "union") + for (const [key, child] of Object.entries(node.members)) + visit(child, [...path, key]); + } + visit(schema, []); + roots[name] = entries; + } + return { version: 2, roots }; +} +function freeze(v, seen = new Set()) { + if (!v || typeof v !== "object" || seen.has(v)) return v; + seen.add(v); + Object.values(v).forEach((item) => freeze(item, seen)); + return Object.freeze(v); +} +module.exports = { + object, + list, + scalar, + opaque, + reference, + union, + encode, + evaluate, + freeze, +}; diff --git a/packages/rescript-relay/__tests__/schema.graphql b/packages/rescript-relay/__tests__/schema.graphql index 85f770d6..deaff309 100644 --- a/packages/rescript-relay/__tests__/schema.graphql +++ b/packages/rescript-relay/__tests__/schema.graphql @@ -288,3 +288,24 @@ input SerializeMultipleCustomScalars { os1s: [ObjectScalar1!]! os2: ObjectScalar2 } + +# Conversion protocol regressions: list depth must not be inferred from JS arrays. +extend type Query { + conversionContract(input: ConversionContractInput): ConversionContract! +} +input ConversionContractInput { + grid: [[ConversionNumber]] + children: [ConversionContractInput] +} +type ConversionContract { + grid: [[ConversionNumber]] + dates: [Datetime] + raw: [[JSON]] + a_b: ConversionNumber + a: ConversionContractNested +} +type ConversionContractNested { + b: Datetime +} + +scalar ConversionNumber diff --git a/packages/rescript-relay/__tests__/utils-contract-tests.js b/packages/rescript-relay/__tests__/utils-contract-tests.js new file mode 100644 index 00000000..8802c450 --- /dev/null +++ b/packages/rescript-relay/__tests__/utils-contract-tests.js @@ -0,0 +1,411 @@ +const { traverser } = require("../src/utils"); + +function freeze(value) { + if (value && typeof value === "object" && !Object.isFrozen(value)) { + Object.values(value).forEach(freeze); + Object.freeze(value); + } + return value; +} + +describe.each([undefined, null])( + "conversion contract (nullable=%s)", + (nullable) => { + test("absent roots, absent instructions, empty and sparse arrays", () => { + expect(traverser(null, null, null, nullable)).toBe(nullable); + expect(traverser(undefined, null, null, nullable)).toBe(nullable); + expect(traverser({}, null, null, nullable)).toStrictEqual({}); + const values = [null, , undefined, 0, false, ""]; + const result = traverser({ values }, null, null, nullable); + expect(result.values).toStrictEqual([nullable, , nullable, 0, false, ""]); + expect(1 in result.values).toBe(false); + expect(traverser([], {}, {}, nullable)).toStrictEqual([]); + }); + + test("frozen inputs, opaque Relay metadata and exact fragment snapshots", () => { + const metadata = { value: null }; + metadata.cycle = metadata; + const input = { + value: null, + nested: { value: null }, + __fragments: metadata, + __fragmentOwner: metadata, + __id: null, + __typename: "User", + __relay_internal__pv__flag: null, + }; + // Freeze the cyclic metadata separately. + Object.freeze(metadata); + freeze(input); + const result = traverser( + input, + { __root: { "": { f: "" }, nested: { f: "" } } }, + {}, + nullable, + ); + expect(result.value).toBe(nullable); + expect(result.__id).toBe(nullable); + expect(result.__relay_internal__pv__flag).toBe(nullable); + expect(result.__fragments).toBe(metadata); + expect(result.__fragmentOwner).toBe(metadata); + expect(result.fragmentRefs).toStrictEqual(input); + expect(result.updatableFragmentRefs).toBe(result.fragmentRefs); + expect(result.nested.fragmentRefs).toStrictEqual(input.nested); + expect(result.nested.updatableFragmentRefs).toBe( + result.nested.fragmentRefs, + ); + expect(result.fragmentRefs).not.toHaveProperty("fragmentRefs"); + expect(input.value).toBeNull(); + expect(input.nested).toStrictEqual({ value: null }); + }); + + test("enum arrays skip nulls and call converters once per non-null element", () => { + const convert = jest.fn((v) => `converted:${v}`); + const input = freeze({ items: ["A", null, "B"], sibling: "C" }); + const result = traverser( + input, + { __root: { items: { e: "enum" }, sibling: { e: "enum" } } }, + { enum: convert }, + nullable, + ); + expect(result).toStrictEqual({ + items: ["converted:A", nullable, "converted:B"], + sibling: "converted:C", + }); + expect(convert.mock.calls).toEqual([["A"], ["B"], ["C"]]); + }); + + test("missing converters leave values available for nullable traversal", () => { + const result = traverser( + freeze({ item: { value: null }, items: [{ value: null }] }), + { __root: { item: { c: "missing" }, items: { u: "missing" } } }, + {}, + nullable, + ); + expect(result).toStrictEqual({ + item: { value: nullable }, + items: [{ value: nullable }], + }); + }); + + test("recursive input unions and arrays use their named instruction root", () => { + const convert = jest.fn((v) => v.toLowerCase()); + const input = freeze({ + items: [{ __$inputUnion: "choice", _0: { status: "A" } }, null], + tree: { status: "B", children: [{ status: "C", children: [] }] }, + }); + const result = traverser( + input, + { + __root: { items: { r: "Input" }, tree: { r: "Tree" } }, + Input: { choice_status: { e: "enum" } }, + Tree: { status: { e: "enum" }, children: { r: "Tree" } }, + }, + { enum: convert }, + nullable, + ); + expect(result).toStrictEqual({ + items: [{ choice: { status: "a" } }, nullable], + tree: { status: "b", children: [{ status: "c", children: [] }] }, + }); + expect(convert.mock.calls).toEqual([["A"], ["B"], ["C"]]); + }); + + test("root union arrays dispatch each member independently", () => { + const union = jest.fn((v) => v); + const input = freeze([ + { __typename: "User", value: null }, + null, + { __typename: "Page", value: null }, + { __typename: "User", value: "ok" }, + ]); + const result = traverser( + input, + { __root: { "": { u: "union" }, User: { f: "" } } }, + { union }, + nullable, + ); + expect(result[0].fragmentRefs).toStrictEqual(input[0]); + expect(result[0].value).toBe(nullable); + expect(result[1]).toBe(nullable); + expect(result[2]).toStrictEqual({ __typename: "Page", value: nullable }); + expect(result[3].fragmentRefs).toStrictEqual(input[3]); + expect(union).toHaveBeenCalledTimes(3); + }); + + test("custom scalar results remain opaque, including falsey values", () => { + for (const output of [ + false, + 0, + "", + null, + undefined, + { value: null }, + [null], + ]) { + const convert = jest.fn(() => output); + const result = traverser( + freeze({ scalar: "input", after: null }), + { __root: { scalar: { c: "scalar" } } }, + { scalar: convert }, + nullable, + ); + expect(result.scalar).toBe(output); + expect(result.after).toBe(nullable); + expect(convert.mock.calls).toEqual([["input"]]); + } + }); + + test("blocked JSON retains identity and nested nulls", () => { + const json = freeze({ value: null, nested: [null] }); + const result = traverser( + freeze({ json, items: [json, null], after: null }), + { __root: { json: { b: "" }, items: { b: "a" } } }, + {}, + nullable, + ); + expect(result.json).toBe(json); + expect(result.items[0]).toBe(json); + expect(result.items[1]).toBe(nullable); + expect(result.after).toBe(nullable); + }); + + test("nested union conversion runs in the correct order in each direction", () => { + const events = []; + const union = (v) => { + events.push("union"); + return { ...v, marker: true }; + }; + const scalar = (v) => { + events.push("scalar"); + return v + 1; + }; + const result = traverser( + freeze({ item: { __typename: "User", value: 1 } }), + { __root: { item: { u: "union" }, item_User_value: { c: "scalar" } } }, + { union, scalar }, + nullable, + ); + expect(result).toStrictEqual({ + item: { __typename: "User", value: 2, marker: true }, + }); + expect(events).toStrictEqual( + nullable === null ? ["union", "scalar"] : ["scalar", "union"], + ); + }); + }, +); + +describe("conversion sharing and repeatability", () => { + test("unchanged roots, objects, lists and nulls retain identity", () => { + const root = freeze({ + id: "1", + nested: { value: 3 }, + list: [{ value: 2 }, "x"], + empty: [], + }); + expect(traverser(root)).toBe(root); + expect(traverser(root.list)).toBe(root.list); + expect(traverser(root.empty)).toBe(root.empty); + const nullable = freeze({ value: null, list: [null] }); + expect(traverser(nullable, {}, {}, null)).toBe(nullable); + }); + + test("copies only ancestors of changes and preserves untouched siblings", () => { + const root = freeze({ + left: { value: 1 }, + right: { value: null }, + list: [{ value: 1 }, { value: null }], + }); + const result = traverser(root); + expect(result).not.toBe(root); + expect(result.left).toBe(root.left); + expect(result.right).not.toBe(root.right); + expect(result.list).not.toBe(root.list); + expect(result.list[0]).toBe(root.list[0]); + expect(result.list[1]).not.toBe(root.list[1]); + }); + + test("does not cache mutable inputs, instructions or converter closures", () => { + const root = { value: 1 }; + const maps = { __root: { value: { c: "scalar" } } }; + let offset = 1; + const converters = { scalar: (v) => v + offset }; + expect(traverser(root, maps, converters).value).toBe(2); + offset = 2; + expect(traverser(root, maps, converters).value).toBe(3); + root.value = 2; + expect(traverser(root, maps, converters).value).toBe(4); + maps.__root.value = {}; + expect(traverser(root, maps, converters).value).toBe(2); + }); + + test("mixed union lists convert untyped siblings independently", () => { + const input = freeze({ + items: [{ __typename: "User", value: null }, { value: null }, null], + }); + const result = traverser( + input, + { __root: { items: { u: "union" } } }, + { union: (v) => v }, + ); + expect(result).toStrictEqual({ + items: [ + { __typename: "User", value: undefined }, + { value: undefined }, + undefined, + ], + }); + }); + + test("fragment refs in lists stay raw and share their updatable ref", () => { + const input = freeze({ items: [{ value: null, nested: { value: null } }] }); + const result = traverser(input, { __root: { items: { f: "" } } }); + expect(result.items[0].fragmentRefs).toStrictEqual(input.items[0]); + expect(result.items[0].fragmentRefs.nested).toBe(input.items[0].nested); + expect(result.items[0].updatableFragmentRefs).toBe( + result.items[0].fragmentRefs, + ); + expect(result.items[0].nested.value).toBeUndefined(); + }); + + test("fragment refs on opaque array elements do not traverse JSON", () => { + const input = freeze({ items: [{ value: null }] }); + const result = traverser(input, { __root: { items: { f: "", b: "a" } } }); + expect(result.items[0].value).toBeNull(); + expect(result.items[0].fragmentRefs).toStrictEqual(input.items[0]); + }); + + test("sparse plural roots preserve holes", () => { + const input = freeze([{ value: null }, , null]); + expect(traverser(input)).toStrictEqual([{ value: undefined }, , undefined]); + }); + + test("converter exceptions propagate without mutating the input", () => { + const input = freeze({ before: null, value: "bad" }); + const error = new Error("bad scalar"); + expect(() => + traverser( + input, + { __root: { value: { c: "scalar" } } }, + { + scalar: () => { + throw error; + }, + }, + ), + ).toThrow(error); + expect(input.before).toBeNull(); + }); + + test("deterministic generated trees match an independent nullable conversion model", () => { + let seed = 0x12345678; + const random = () => { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; + return seed; + }; + function generate(depth) { + if (!depth) + return [null, undefined, false, 0, "", "text", 42][random() % 7]; + return { + value: generate(0), + child: generate(depth - 1), + items: Array.from({ length: random() % 5 }, () => generate(depth - 1)), + }; + } + function reference(value, nullable) { + if (value == null) return nullable; + if (Array.isArray(value)) return value.map((v) => reference(v, nullable)); + if (typeof value === "object") + return Object.fromEntries( + Object.entries(value).map(([k, v]) => [k, reference(v, nullable)]), + ); + return value; + } + for (let i = 0; i < 100; i++) { + const input = freeze(generate(3)); + for (const nullable of [null, undefined]) { + expect(traverser(input, {}, {}, nullable)).toStrictEqual( + reference(input, nullable), + ); + } + } + }); +}); + +test("generated combinations of enums, scalar lists, recursion and union members have exact outputs", () => { + for (let size = 0; size < 25; size++) { + const rows = Array.from({ length: size }, (_, i) => ({ + __typename: i % 2 ? "User" : "Page", + status: i % 3 ? "ON" : null, + scalar: String(i), + json: { nullable: null }, + })); + const input = freeze({ rows, after: null }); + for (const nullable of [undefined, null]) { + let scalarCalls = 0; + const converters = { + union: (v) => v, + enum: (v) => v.toLowerCase(), + scalar: (v) => { + scalarCalls++; + return Number(v); + }, + }; + const instructions = { rows: { u: "union" } }; + for (const member of ["User", "Page"]) { + instructions[`rows_${member}_status`] = { e: "enum" }; + instructions[`rows_${member}_scalar`] = { c: "scalar" }; + instructions[`rows_${member}_json`] = { b: "" }; + } + expect( + traverser(input, { __root: instructions }, converters, nullable), + ).toStrictEqual({ + rows: rows.map((row) => ({ + ...row, + status: row.status === null ? nullable : "on", + scalar: Number(row.scalar), + })), + after: nullable, + }); + expect(scalarCalls).toBe(size); + } + } +}); + +test("recursive array roots are converted exactly once, including nullable scalar results", () => { + const input = freeze({ items: [{ value: "wire" }], after: null }); + const output = { value: null }; + const scalar = jest.fn(() => output); + const result = traverser( + input, + { __root: { items: { r: "Input" } }, Input: { value: { c: "scalar" } } }, + { scalar }, + ); + expect(result.items[0].value).toBe(output); + expect(result.items[0].value.value).toBeNull(); + expect(result.after).toBeUndefined(); + expect(scalar).toHaveBeenCalledTimes(1); +}); + +test("legacy custom scalar lists normalize nulls and preserve nested option markers without invoking callbacks", () => { + const marker = { BS_PRIVATE_NESTED_SOME_NONE: 0 }; + const scalar = jest.fn((v) => ({ value: v })); + const input = freeze({ items: ["a", null, undefined, marker, "b"] }); + const output = traverser( + input, + { __root: { items: { ca: "scalar" } } }, + { scalar }, + ); + expect(output.items).toStrictEqual([ + { value: "a" }, + undefined, + undefined, + marker, + { value: "b" }, + ]); + expect(scalar.mock.calls).toEqual([ + ["a", 0, input.items], + ["b", 4, input.items], + ]); +}); diff --git a/packages/rescript-relay/__tests__/utils-module-tests.js b/packages/rescript-relay/__tests__/utils-module-tests.js new file mode 100644 index 00000000..55c5d2b5 --- /dev/null +++ b/packages/rescript-relay/__tests__/utils-module-tests.js @@ -0,0 +1,52 @@ +const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const { execFile } = require("child_process"); +const execFileAsync = require("util").promisify(execFile); +const { + buildConversionModules, +} = require("../scripts/build-conversion-modules"); + +test("release ESM and CommonJS exports execute the same legacy and prepared conversions", async () => { + const directory = fs.mkdtempSync( + path.join(os.tmpdir(), "relay-conversion-modules-"), + ); + try { + buildConversionModules(directory); + // Test native ESM, bypassing Jest's dynamic-import-to-require transform. + const program = ` + import assert from 'node:assert/strict'; + import {pathToFileURL} from 'node:url'; + import {createRequire} from 'node:module'; + const require=createRequire(import.meta.url); + const esm=await import(pathToFileURL(process.argv[1])); + const cjs=require(process.argv[2]); + assert.deepEqual(Object.keys(esm).sort(),Object.keys(cjs).sort()); + const data={items:['1',null],after:null}; + const legacy={__root:{items:{ca:'scalar'}}}; + const plan={version:2,roots:{__root:[{path:['items'],list:1,scalar:'scalar'}]}}; + for(const nullable of [null,undefined]) { + const expected={items:[1,nullable],after:nullable}; + for(const module of [esm,cjs]) { + assert.deepEqual(module.traverser(data,legacy,{scalar:Number},nullable),expected); + assert.deepEqual(module.convertWithoutPlan({value:null},nullable),{value:nullable}); + assert.deepEqual(module.runConversion(module.prepareConversion(plan,{scalar:Number},nullable),data),expected); + } + } + assert.deepEqual(data,{items:['1',null],after:null}); + `; + await execFileAsync( + process.execPath, + [ + "--input-type=module", + "-e", + program, + path.join(directory, "utils.mjs"), + require.resolve("../src/utils"), + ], + { stdio: "pipe" }, + ); + } finally { + fs.rmSync(directory, { recursive: true, force: true }); + } +}); diff --git a/packages/rescript-relay/__tests__/utils-path-tests.js b/packages/rescript-relay/__tests__/utils-path-tests.js new file mode 100644 index 00000000..82bda2f6 --- /dev/null +++ b/packages/rescript-relay/__tests__/utils-path-tests.js @@ -0,0 +1,171 @@ +const { traverser } = require("../src/utils"); + +describe("conversion instruction paths", () => { + test("handles nested underscore paths, nullability, enums, scalars, arrays, and fragments without mutating input", () => { + const input = { + account_details: { + status_level: "2", + optional_label: null, + scores: ["3", null], + profile_data: { + created_at: "4", + }, + }, + }; + const originalInput = JSON.parse(JSON.stringify(input)); + + const result = traverser( + input, + { + __root: { + "": { f: "" }, + account_details: { f: "" }, + account_details_status_level: { e: "Status" }, + account_details_optional_label: { n: "" }, + account_details_scores: { n: "", e: "Status" }, + account_details_profile_data: { f: "" }, + account_details_profile_data_created_at: { c: "Int" }, + }, + }, + { + Status: (value) => Number(value), + Int: (value) => Number(value), + }, + undefined + ); + + expect(result).toEqual({ + fragmentRefs: expect.any(Object), + updatableFragmentRefs: expect.any(Object), + account_details: { + fragmentRefs: expect.any(Object), + updatableFragmentRefs: expect.any(Object), + status_level: 2, + optional_label: undefined, + scores: [3, undefined], + profile_data: { + fragmentRefs: expect.any(Object), + updatableFragmentRefs: expect.any(Object), + created_at: 4, + }, + }, + }); + expect(input).toEqual(originalInput); + }); + + test("handles nested unions and root unions for objects and arrays", () => { + const converters = { + Node: (value) => ({ kind: value.__typename, value }), + Status: (value) => Number(value), + }; + const nestedInput = { + nodes: [ + null, + { __typename: "User", status_code: "5" }, + { __typename: "Team", status_code: "6" }, + ], + }; + const nestedOriginal = JSON.parse(JSON.stringify(nestedInput)); + const instructions = { + __root: { + nodes: { n: "", u: "Node" }, + nodes_User: { f: "" }, + nodes_User_status_code: { e: "Status" }, + nodes_Team_status_code: { e: "Status" }, + }, + }; + + expect( + traverser( + nestedInput, + instructions, + converters, + undefined + ) + ).toEqual({ + nodes: [ + undefined, + { + kind: "User", + value: { + fragmentRefs: expect.any(Object), + updatableFragmentRefs: expect.any(Object), + __typename: "User", + status_code: 5, + }, + }, + { + kind: "Team", + value: { __typename: "Team", status_code: 6 }, + }, + ], + }); + expect(nestedInput).toEqual(nestedOriginal); + + const rootInstructions = { + __root: { + "": { u: "Node" }, + User: { f: "" }, + User_status_code: { e: "Status" }, + }, + }; + const user = { __typename: "User", status_code: "7" }; + + expect( + traverser(user, rootInstructions, converters, undefined) + ).toEqual({ + kind: "User", + value: { + fragmentRefs: expect.any(Object), + updatableFragmentRefs: expect.any(Object), + __typename: "User", + status_code: 7, + }, + }); + expect( + traverser([user, null], rootInstructions, converters, undefined) + ).toEqual([ + { + kind: "User", + value: { + fragmentRefs: expect.any(Object), + updatableFragmentRefs: expect.any(Object), + __typename: "User", + status_code: 7, + }, + }, + undefined, + ]); + expect(user).toEqual({ __typename: "User", status_code: "7" }); + }); + + test("uses the selected root object instruction map", () => { + const input = { + filter_value: { + minimum_score: "8", + optional_score: null, + }, + }; + + expect( + traverser( + input, + { + Variables: { filter_value: { r: "FilterInput" } }, + FilterInput: { + minimum_score: { c: "Int" }, + optional_score: { n: "" }, + }, + }, + { Int: (value) => Number(value) }, + undefined, + "Variables" + ) + ).toEqual({ + filter_value: { minimum_score: 8, optional_score: undefined }, + }); + expect(input).toEqual({ + filter_value: { minimum_score: "8", optional_score: null }, + }); + }); +}); diff --git a/packages/rescript-relay/__tests__/utils-prepared-tests.js b/packages/rescript-relay/__tests__/utils-prepared-tests.js new file mode 100644 index 00000000..c70b8034 --- /dev/null +++ b/packages/rescript-relay/__tests__/utils-prepared-tests.js @@ -0,0 +1,739 @@ +const { prepareConversion, runConversion } = require("../src/utils"); +const { + object, + list, + scalar, + opaque, + reference, + union, + encode, + evaluate, + freeze, +} = require("./helpers/conversionModel"); +function prepare(schema, converters = {}, nullable, extra = {}) { + const convert = prepareConversion( + encode({ __root: schema, ...extra }), + converters, + nullable, + ); + return (value) => runConversion(convert, value); +} + +describe.each([undefined, null])( + "prepared conversion (nullable=%s)", + (nullable) => { + test("field boundaries cannot collide with underscores or union type names", () => { + const schema = object({ + a_b: scalar("a"), + a: object({ b: scalar("b") }), + members: union("union", { + User_Profile: object({ created_at: scalar("a") }), + }), + }); + const input = freeze({ + a_b: "x", + a: { b: "y" }, + members: { __typename: "User_Profile", created_at: "z" }, + }); + const c = { a: (v) => "a:" + v, b: (v) => "b:" + v, union: (v) => v }; + expect(prepare(schema, c, nullable)(input)).toStrictEqual({ + a_b: "a:x", + a: { b: "b:y" }, + members: { __typename: "User_Profile", created_at: "a:z" }, + }); + }); + + test.each([0, 1, 2, 3])( + "explicit list depth %i preserves array-valued scalar boundaries", + (depth) => { + let schema = scalar("box"); + let wire = "value"; + let expected = ["value"]; + for (let i = 0; i < depth; i++) { + schema = list(schema); + wire = [wire, null, undefined]; + expected = [expected, nullable, nullable]; + } + const box = jest.fn((v) => [v]); + const output = prepare( + object({ value: schema }), + { box }, + nullable, + )(freeze({ value: wire })); + expect(output).toStrictEqual({ value: expected }); + expect(box).toHaveBeenCalledTimes(1); + // The opposite direction can have an array-valued scalar as its input. + const unbox = jest.fn((v) => v[0]); + let boxed = ["value"]; + let unboxed = "value"; + for (let i = 0; i < depth; i++) { + boxed = [boxed, null]; + unboxed = [unboxed, nullable]; + } + expect( + prepare( + object({ value: schema }), + { box: unbox }, + nullable, + )({ value: boxed }), + ).toStrictEqual({ value: unboxed }); + expect(unbox).toHaveBeenCalledTimes(1); + }, + ); + + test("sparse lists, optional markers, missing keys and falsey values stay distinct", () => { + const marker = { BS_PRIVATE_NESTED_SOME_NONE: 0 }; + const input = freeze({ + items: [null, , undefined, marker, 0, false, ""], + marker, + missingSibling: null, + }); + const fn = jest.fn((v) => v); + const output = prepare( + object({ + items: list(scalar("scalar")), + marker: scalar("scalar"), + absent: scalar("scalar"), + }), + { scalar: fn }, + nullable, + )(input); + expect(output.items).toStrictEqual([ + nullable, + , + nullable, + marker, + 0, + false, + "", + ]); + expect(output.marker).toBe(marker); + expect(output).not.toHaveProperty("absent"); + expect(fn.mock.calls).toEqual([[0], [false], [""]]); + }); + + test("nested JSON list depth differs from arrays inside JSON", () => { + const json = freeze([null, { x: null }]); + const input = freeze({ items: [[json, null], null] }); + const output = prepare( + object({ items: list(list(opaque)) }), + {}, + nullable, + )(input); + expect(output.items[0][0]).toBe(json); + expect(output.items[0][1]).toBe(nullable); + expect(output.items[1]).toBe(nullable); + }); + + test("root and nested unions run callbacks in the same order", () => { + for (const nested of [false, true]) { + const schema = union("union", { + User: object({ value: scalar("scalar") }, true), + }); + const calls = []; + const c = { + union: (v) => { + calls.push("union"); + return { ...v, extra: 1 }; + }, + scalar: (v) => { + calls.push("scalar"); + return v + 1; + }, + }; + const input = freeze({ __typename: "User", value: 1 }); + const output = prepare( + nested ? object({ node: schema }) : schema, + c, + nullable, + )(nested ? { node: input } : input); + const node = nested ? output.node : output; + expect(node.value).toBe(2); + expect(node.extra).toBe(1); + expect(calls).toEqual( + nullable === null ? ["union", "scalar"] : ["scalar", "union"], + ); + expect(node.fragmentRefs.value).toBe(1); + expect(node.updatableFragmentRefs).toBe(node.fragmentRefs); + } + }); + + test("unknown union members, nullable members and plural roots are independent", () => { + const schema = union("union", { + User: object({ value: scalar("scalar") }, true), + }); + const c = { + union: (v) => + v.__typename === "User" ? v : { ...v, __typename: "__unselected" }, + scalar: (v) => v + 1, + }; + const input = freeze([ + { __typename: "User", value: 1 }, + null, + { __typename: "Page", value: null }, + ]); + const output = prepare(schema, c, nullable)(input); + expect(output[0].value).toBe(2); + expect(output[1]).toBe(nullable); + expect(output[2]).toStrictEqual({ + __typename: "__unselected", + value: nullable, + }); + expect(output[2]).not.toHaveProperty("fragmentRefs"); + }); + + test("recursive references reset scope and unwrap input unions at every depth", () => { + const definitions = { + Tree: object({ + value: scalar("scalar"), + children: list(reference("Tree")), + }), + Choice: object({ tree: reference("Tree") }), + }; + const scalarFn = jest.fn((v) => Number(v)); + const input = freeze({ + choice: { + __$inputUnion: "tree", + _0: { value: "1", children: [{ value: "2", children: [] }, null] }, + }, + }); + expect( + prepare( + object({ choice: reference("Choice") }), + { scalar: scalarFn }, + nullable, + definitions, + )(input), + ).toStrictEqual({ + choice: { + tree: { value: 1, children: [{ value: 2, children: [] }, nullable] }, + }, + }); + expect(scalarFn.mock.calls).toEqual([["1"], ["2"]]); + }); + + test("shared objects are converted by path, not by identity", () => { + const shared = freeze({ value: "x" }); + const input = freeze({ a: shared, b: shared }); + expect( + prepare( + object({ + a: object({ value: scalar("a") }), + b: object({ value: scalar("b") }), + }), + { a: (v) => "a" + v, b: (v) => "b" + v }, + nullable, + )(input), + ).toStrictEqual({ a: { value: "ax" }, b: { value: "bx" } }); + }); + + test("scalar output and cyclic metadata remain opaque; callbacks can reenter conversion", () => { + const cyclic = {}; + cyclic.self = cyclic; + freeze(cyclic); + let convert; + const c = { + scalar: (v) => + v === "outer" ? convert({ value: "inner", __owner: cyclic }) : cyclic, + }; + convert = prepare(object({ value: scalar("scalar") }), c, nullable); + const output = convert(freeze({ value: "outer", __owner: cyclic })); + expect(output.value.value).toBe(cyclic); + expect(output.__owner).toBe(cyclic); + }); + + test("callbacks follow data property order, independent of instruction order", () => { + const schema = object({ + a: scalar("scalar"), + b: scalar("scalar"), + items: list(scalar("scalar")), + }); + const plan = encode({ __root: schema }); + for (const reverse of [false, true]) { + const c = jest.fn((v) => v + 1); + const entries = plan.roots.__root.slice(); + if (reverse) entries.reverse(); + const convert = prepareConversion( + { version: 2, roots: { __root: entries } }, + { scalar: c }, + nullable, + ); + expect( + convert(freeze({ b: 2, items: [3, null, 4], a: 1 })), + ).toStrictEqual({ b: 3, items: [4, nullable, 5], a: 2 }); + expect(c.mock.calls).toEqual([[2], [3], [4], [1]]); + } + }); + + test("callback errors propagate exactly and stop later siblings without source mutation", () => { + const error = new Error("scalar failure"); + const after = jest.fn((v) => v); + const input = freeze({ before: null, value: "bad", after: "untouched" }); + const convert = prepare( + object({ value: scalar("fail"), after: scalar("after") }), + { + fail: () => { + throw error; + }, + after, + }, + nullable, + ); + expect(() => convert(input)).toThrow(error); + expect(after).not.toHaveBeenCalled(); + expect(input.before).toBeNull(); + }); + }, +); + +test("prepared plans snapshot metadata and callback bindings, but not callback closure state or data", () => { + const plan = encode({ __root: object({ value: scalar("scalar") }) }); + let offset = 1; + const callbacks = { scalar: (v) => v + offset }; + const convert = prepareConversion(plan, callbacks, undefined); + plan.roots.__root.length = 0; + callbacks.scalar = () => 999; + expect(convert({ value: 1 })).toStrictEqual({ value: 2 }); + offset = 2; + expect(convert({ value: 1 })).toStrictEqual({ value: 3 }); +}); + +test("unchanged branches and null-prototype dictionaries preserve data without inherited selections", () => { + const convert = prepare(object()); + const root = freeze({ items: [{ x: 1 }], value: 0 }); + expect(convert(root)).toBe(root); + const raw = Object.assign(Object.create(null), { + constructor: null, + toString: null, + }); + expect(convert(raw)).toEqual({ constructor: undefined, toString: undefined }); + const inherited = Object.create({ hidden: null }); + inherited.value = null; + expect(convert(inherited)).toStrictEqual({ value: undefined }); + const special = JSON.parse('{"__proto__":{"polluted":true},"value":null}'); + const output = convert(special); + expect(Object.getPrototypeOf(output)).toBe(Object.prototype); + expect(Object.hasOwn(output, "__proto__")).toBe(true); + expect({}.polluted).toBeUndefined(); +}); + +describe("prepared plan validation", () => { + test.each([ + [null, {}, undefined, /version 2/], + [{ version: 1, roots: {} }, {}, undefined, /version 2/], + [{ version: 2, roots: {} }, {}, undefined, /Missing conversion root/], + [ + { version: 2, roots: { __root: {} } }, + {}, + undefined, + /Invalid conversion root/, + ], + [ + { version: 2, roots: { __root: [{}] } }, + {}, + undefined, + /Invalid conversion path/, + ], + [ + { version: 2, roots: { __root: [{ path: [1] }] } }, + {}, + undefined, + /Invalid conversion path/, + ], + [ + { version: 2, roots: { __root: [{ path: [], typo: true }] } }, + {}, + undefined, + /Unknown conversion instruction/, + ], + [{ version: 2, roots: { __root: [] } }, {}, false, /nullable sentinel/], + [ + { version: 2, roots: { __root: [{ path: ["x"], scalar: "toString" }] } }, + {}, + undefined, + /Missing conversion callback/, + ], + [ + { + version: 2, + roots: { __root: [{ path: ["x"], reference: "missing" }] }, + }, + {}, + undefined, + /Missing conversion reference/, + ], + [ + { + version: 2, + roots: { + __root: [ + { path: ["x"], scalar: "a" }, + { path: ["x"], scalar: "b" }, + ], + }, + }, + {}, + undefined, + /Conflicting conversion instruction/, + ], + ])( + "rejects invalid plan %# before seeing data", + (plan, callbacks, nullable, error) => { + expect(() => prepareConversion(plan, callbacks, nullable)).toThrow(error); + }, + ); + test.each([ + [{ scalar: "a", opaque: true }, /Conflicting conversion operations/], + [{ scalar: 1 }, /Invalid conversion scalar/], + [{ union: 1 }, /Invalid conversion union/], + [{ reference: 1 }, /Invalid conversion reference/], + [{ opaque: false }, /Invalid conversion opaque/], + [{ fragments: false }, /Invalid conversion fragments/], + [{ list: -1 }, /Invalid conversion list depth/], + [{ list: 1.5 }, /Invalid conversion list depth/], + [{ list: "1" }, /Invalid conversion list depth/], + [{ scalar: "a", fragments: true }, /Leaf conversion/], + [{ union: "a", fragments: true }, /Union fragments/], + ])("rejects conflicting or malformed operation %#", (instruction, error) => { + expect(() => + prepareConversion( + { version: 2, roots: { __root: [{ path: ["x"], ...instruction }] } }, + { a: (v) => v }, + ), + ).toThrow(error); + }); + test("rejects child instructions underneath opaque/scalar/reference boundaries", () => { + expect(() => + prepareConversion({ + version: 2, + roots: { + __root: [ + { path: ["x"], opaque: true }, + { path: ["x", "child"], fragments: true }, + ], + }, + }), + ).toThrow(/Leaf conversion/); + }); + test("identical entries can be repeated and explicitly named roots selected", () => { + const entry = { path: ["x"], scalar: "a" }; + expect( + prepareConversion( + { version: 2, roots: { Other: [entry, entry] } }, + { a: (v) => v + 1 }, + undefined, + "Other", + )({ x: 1 }), + ).toStrictEqual({ x: 2 }); + }); +}); + +test("deterministic generated schema trees agree with independent evaluator and callback traces", () => { + let seed = 0x941bf; + const random = () => { + seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; + return seed; + }; + function generate(depth) { + if (!depth) + return [scalar("number"), scalar("box"), opaque, object()][random() % 4]; + switch (random() % 5) { + case 0: + return list(generate(depth - 1)); + case 1: + return union("union", { + User: object({ a_b: generate(depth - 1) }), + Page: object({ value: generate(depth - 1) }, true), + }); + default: + return object( + { + a_b: generate(depth - 1), + a: object({ b: generate(depth - 1) }), + other: generate(0), + }, + random() % 3 === 0, + ); + } + } + function payload(schema) { + const n = random() % 10; + if (n === 0) return null; + if (n === 1) return undefined; + switch (schema.kind) { + case "scalar": + return String(random() % 100); + case "opaque": + return [null, { x: null }]; + case "list": + return Array.from({ length: random() % 4 }, () => payload(schema.item)); + case "union": { + const type = random() % 2 ? "User" : "Page"; + return { ...payload(schema.members[type]), __typename: type }; + } + case "object": + return Object.fromEntries( + Object.entries(schema.fields) + .reverse() + .map(([key, field]) => [key, payload(field)]), + ); + } + } + for (let i = 0; i < 150; i++) { + const schema = object({ root: generate(3) }); + const input = freeze(payload(schema)); + const definitions = { __root: schema }; + const plan = freeze(encode(definitions)); + for (const nullable of [null, undefined]) { + const actualTrace = [], + expectedTrace = []; + const callbacks = (trace) => ({ + number: (v) => { + trace.push(["number", v]); + return Number(v); + }, + box: (v) => { + trace.push(["box", v]); + return [v]; + }, + union: (v) => { + trace.push(["union", v]); + return v; + }, + }); + const result = prepareConversion( + plan, + callbacks(actualTrace), + nullable, + )(input); + expect(result).toStrictEqual( + evaluate( + schema, + input, + definitions, + callbacks(expectedTrace), + nullable, + ), + ); + expect(actualTrace).toStrictEqual(expectedTrace); + } + } +}); + +test("read/write round trips retain nullable structure, nested lists and opaque scalar values", () => { + const schema = object({ + nested: list(list(scalar("number"))), + boxed: list(scalar("box")), + json: opaque, + }); + const wire = freeze({ + nested: [["1", null, "2"], null], + boxed: ["x", null, "y"], + json: { value: null }, + }); + const read = prepare( + schema, + { number: (v) => Number(v), box: (v) => [v] }, + undefined, + ); + const write = prepare( + schema, + { number: (v) => String(v), box: (v) => v[0] }, + null, + ); + const decoded = read(wire); + expect(decoded.boxed[0]).toStrictEqual(["x"]); + expect(write(decoded)).toStrictEqual(wire); + expect(decoded.json).toBe(wire.json); +}); + +// Native scalar lists have list wrappers but no scalar callback instruction. +test("native scalar list plans preserve primitives and normalize nullable members", () => { + const convert = prepareConversion( + { version: 2, roots: { __root: [{ path: ["values"], list: 1 }] } }, + {}, + undefined, + ); + const unchanged = freeze({ + values: ["long string", 0, false, "", undefined], + }); + expect(convert(unchanged)).toBe(unchanged); + expect(convert(freeze({ values: ["x", null, [null]] }))).toStrictEqual({ + values: ["x", undefined, [undefined]], + }); +}); + +describe.each([null, undefined])( + "shared conversion without instructions (%s)", + (nullable) => { + test("matches ordinary preparation without retaining or mutating values", () => { + const { convertWithoutPlan } = require("../src/utils"); + const option = { BS_PRIVATE_NESTED_SOME_NONE: 0 }; + const metadata = { opaque: null }; + const input = freeze({ + list: [null, undefined, , { value: null }, option], + __metadata: metadata, + }); + const expected = { + list: [nullable, nullable, , { value: nullable }, option], + __metadata: metadata, + }; + expect(convertWithoutPlan(input, nullable)).toStrictEqual(expected); + expect(convertWithoutPlan(input, nullable)).toStrictEqual( + prepareConversion( + { version: 2, roots: { __root: [] } }, + {}, + nullable, + )(input), + ); + expect(convertWithoutPlan(null, nullable)).toBe(nullable); + expect(convertWithoutPlan([input], nullable)).toStrictEqual([expected]); + expect(convertWithoutPlan(option, nullable)).toBe(option); + const unchanged = freeze({ values: [0, false, "", nullable] }); + expect(convertWithoutPlan(unchanged, nullable)).toBe(unchanged); + const mutable = { value: null }; + convertWithoutPlan(mutable, nullable); + mutable.value = "updated"; + expect(convertWithoutPlan(mutable, nullable)).toBe(mutable); + }); + }, +); + +test("callback slots stay local to each converter and snapshot their bindings", () => { + const schema = object({ number: scalar("0"), text: scalar("1") }); + const callbacks = { 0: (v) => Number(v), 1: (v) => [v] }; + const first = prepare(schema, callbacks); + const second = prepare(schema, { + 0: (v) => Number(v) + 1, + 1: (v) => ({ value: v }), + }); + callbacks[0] = () => 999; + expect(first({ number: "1", text: "a" })).toStrictEqual({ + number: 1, + text: ["a"], + }); + expect(second({ number: "1", text: "a" })).toStrictEqual({ + number: 2, + text: { value: "a" }, + }); + expect(() => prepare(object({ value: scalar("1") }), { 0: Number })).toThrow( + /Missing conversion callback/, + ); + expect(() => prepare(object({ value: scalar("toString") }), {})).toThrow( + /Missing conversion callback/, + ); +}); + +describe.each([null, undefined])( + "omitted native-list hints (nullable=%s)", + (nullable) => { + test.each([1, 2, 3])( + "preserves native values, holes and nullable wrappers at depth %i", + (depth) => { + const { convertWithoutPlan } = require("../src/utils"); + let values = [ + 0, + false, + "", + null, + undefined, + , + { BS_PRIVATE_NESTED_SOME_NONE: 0 }, + ]; + let schema = list(scalar("identity")); + for (let i = 1; i < depth; i++) { + values = [values, null, undefined, , []]; + schema = list(schema); + } + const input = freeze({ values, __metadata: { opaque: null } }); + const expected = evaluate( + object({ values: schema }), + input, + {}, + { identity: (value) => value }, + nullable, + ); + const explicit = prepareConversion( + { + version: 2, + roots: { __root: [{ path: ["values"], list: depth }] }, + }, + undefined, + nullable, + ); + expect(explicit(input)).toStrictEqual(expected); + expect(convertWithoutPlan(input, nullable)).toStrictEqual(expected); + }, + ); + }, +); + +describe.each([undefined, null])("specialized traversal (%s)", (nullable) => { + test("opaque lists normalize only present nullish elements and retain nested identities", () => { + const cyclic = { value: null }; + cyclic.self = cyclic; + const nested = [null, undefined]; + const marker = { BS_PRIVATE_NESTED_SOME_NONE: 0 }; + const values = [null, , undefined, cyclic, nested, marker, 0, false, "", null, undefined]; + const input = freeze({ values }); + const convert = prepare(object({ values: list(opaque) }), {}, nullable); + const output = convert(input); + expect(output).toStrictEqual({ + values: [nullable, , nullable, cyclic, nested, marker, 0, false, "", nullable, nullable], + }); + expect(output).not.toBe(input); + expect(output.values).not.toBe(values); + expect(1 in output.values).toBe(false); + for (const index of [3, 4, 5]) expect(output.values[index]).toBe(values[index]); + expect(values[0]).toBe(null); + expect(values[2]).toBeUndefined(); + const unchanged = freeze({ values: [nullable, , cyclic, nested, marker] }); + expect(convert(unchanged)).toBe(unchanged); + const empty = freeze({ values: [] }); + expect(convert(empty)).toBe(empty); + }); + + test("opaque sparse lists preserve inherited array elements when copying", () => { + const inherited = freeze({ value: null }); + const prototype = Object.create(Array.prototype); + prototype[1] = inherited; + const values = [null, , undefined]; + Object.setPrototypeOf(values, prototype); + Object.freeze(values); + const output = prepare(list(opaque), {}, nullable)(values); + expect(output).toStrictEqual([nullable, inherited, nullable]); + expect(output[1]).toBe(inherited); + expect(Object.prototype.hasOwnProperty.call(values, 1)).toBe(false); + expect(values[0]).toBe(null); + expect(values[2]).toBeUndefined(); + }); + + test("generic objects skip inherited fields and metadata while preserving own-key order", () => { + const metadata = {}; + metadata.self = metadata; + const input = Object.assign(Object.create({ inherited: { value: null } }), { + "2": null, + z: undefined, + __metadata: metadata, + stable: { value: 1 }, + a: null, + }); + Object.defineProperty(input, "__proto__", { + value: { value: null, other: undefined }, + enumerable: true, + }); + freeze(input); + const output = prepare(object(), {}, nullable)(input); + expect(Object.keys(output)).toEqual(Object.keys(input)); + expect(output).not.toHaveProperty("inherited"); + expect(output.__metadata).toBe(metadata); + expect(output.stable).toBe(input.stable); + expect(Object.getPrototypeOf(output)).toBe(Object.prototype); + expect(Object.prototype.hasOwnProperty.call(output, "__proto__")).toBe(true); + // Double-underscore keys are opaque metadata, including __proto__. + expect(output.__proto__).toBe(input.__proto__); + expect(output["2"]).toBe(nullable); + expect(output.z).toBe(nullable); + expect(output.a).toBe(nullable); + }); +}); diff --git a/packages/rescript-relay/__tests__/utils-scalar-ordering-tests.js b/packages/rescript-relay/__tests__/utils-scalar-ordering-tests.js new file mode 100644 index 00000000..6d21347d --- /dev/null +++ b/packages/rescript-relay/__tests__/utils-scalar-ordering-tests.js @@ -0,0 +1,157 @@ +const { traverser, prepareConversion } = require("../src/utils"); +const { freeze } = require("./helpers/conversionModel"); + +function permutations(values) { + if (values.length === 0) return [[]]; + return values.flatMap((value, index) => + permutations(values.filter((_, i) => i !== index)).map(rest => [value, ...rest]), + ); +} +const fields = ["single", "many", "nested", "nullable", "last"]; +const orders = permutations(fields); +const entries = [ + { path: ["single"], scalar: "single" }, + { path: ["many"], list: 1, scalar: "many" }, + { path: ["nested"], reference: "Input" }, + { path: ["last"], scalar: "last" }, +]; +const legacyEntries = [ + ["single", { c: "single" }], + ["many", { ca: "many" }], + ["nested", { r: "Input" }], + ["last", { c: "last" }], +]; + +describe.each(["legacy", "prepared"])("%s custom scalar ordering", runtime => { + test.each([undefined, null])("all 120 sibling orders and both instruction orders (nullable=%s)", nullable => { + for (const reversePlan of [false, true]) { + for (const order of orders) { + const source = { + single: [1], // One array-backed scalar, not a GraphQL list. + many: [[2], null, , [3]], // A sparse GraphQL list of array-backed scalars. + nested: { value: [4], after: null }, + nullable: null, + last: [5], + }; + const input = freeze(Object.fromEntries(order.map(key => [key, source[key]]))); + const trace = []; + const outputs = {}; + const callbacks = Object.fromEntries(["single", "many", "nested", "last"].map(name => [ + name, + value => { + trace.push([name, value]); + // Returning arrays/objects with nulls catches accidental traversal + // of parser/serializer results, including repeated conversion. + const result = freeze({ converted: value[0], untouched: [null] }); + outputs[value[0]] = result; + return result; + }, + ])); + const ordered = values => reversePlan ? values.slice().reverse() : values; + const convert = runtime === "prepared" + ? prepareConversion({ version: 2, roots: { + __root: ordered(entries), + Input: [{ path: ["value"], scalar: "nested" }], + } }, callbacks, nullable) + : value => traverser(value, { + __root: Object.fromEntries(ordered(legacyEntries)), + Input: { value: { c: "nested" } }, + }, callbacks, nullable); + const expectedTrace = order.flatMap(key => ({ + single: [["single", source.single]], + many: [["many", source.many[0]], ["many", source.many[3]]], + nested: [["nested", source.nested.value]], + nullable: [], + last: [["last", source.last]], + })[key]); + for (let call = 0; call < 2; call++) { + trace.length = 0; + const result = convert(input); + expect(trace).toStrictEqual(expectedTrace); + expect(result).toStrictEqual({ + single: outputs[1], + many: [outputs[2], nullable, , outputs[3]], + nested: { value: outputs[4], after: nullable }, + nullable, + last: outputs[5], + }); + expect(result.single).toBe(outputs[1]); + expect(result.many[0]).toBe(outputs[2]); + expect(Object.keys(result)).toEqual(order); + } + expect(input.single).toBe(source.single); + expect(input.nested.after).toBeNull(); + expect(input.nullable).toBeNull(); + } + } + }); + test.each([undefined, null])("empty scalar arrays differ from empty GraphQL lists (nullable=%s)", nullable => { + const single = []; + const element = []; + const callback = jest.fn(value => ({ raw: value })); + const input = freeze({ single, emptyList: [], list: [element, null], after: null }); + const convert = runtime === "prepared" + ? prepareConversion({ version: 2, roots: { __root: [ + { path: ["single"], scalar: "scalar" }, + { path: ["emptyList"], list: 1, scalar: "scalar" }, + { path: ["list"], list: 1, scalar: "scalar" }, + ] } }, { scalar: callback }, nullable) + : value => traverser(value, { __root: { + single: { c: "scalar" }, emptyList: { ca: "scalar" }, list: { ca: "scalar" }, + } }, { scalar: callback }, nullable); + const result = convert(input); + expect(callback).toHaveBeenCalledTimes(2); + expect(callback.mock.calls[0][0]).toBe(single); + expect(callback.mock.calls[1][0]).toBe(element); + expect(result.single.raw).toBe(single); + expect(result.emptyList).toEqual([]); + expect(result.list[0].raw).toBe(element); + expect(result.list[1]).toBe(nullable); + expect(result.after).toBe(nullable); + }); + +}); + +describe.each([undefined, null])("prepared scalar result boundary (nullable=%s)", nullable => { + test("all scalar results remain opaque at root, field and list positions", () => { + const results = [false, 0, "", null, undefined, [null], { value: null }, + { BS_PRIVATE_NESTED_SOME_NONE: 0 }, new Date("2020-01-01")]; + for (const returned of results) { + for (const position of ["root", "field", "list"]) { + const callback = jest.fn(() => returned); + const convert = prepareConversion({ version: 2, roots: { __root: [ + { path: position === "root" ? [] : ["value"], scalar: "scalar", + ...(position === "list" ? { list: 1 } : {}) }, + ] } }, { scalar: callback }, nullable); + const result = convert(freeze(position === "root" ? "wire" : { + value: position === "list" ? ["wire", null] : "wire", after: null, + })); + expect(position === "root" ? result : position === "list" ? result.value[0] : result.value).toBe(returned); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith("wire"); + if (position !== "root") expect(result.after).toBe(nullable); + if (position === "list") expect(result.value[1]).toBe(nullable); + } + } + }); + + test("list callback errors stop later elements and siblings, and do not poison subsequent calls", () => { + const failure = new Error("scalar failed"); + const trace = []; + const convert = prepareConversion({ version: 2, roots: { __root: [ + { path: ["values"], list: 1, scalar: "scalar" }, + { path: ["after"], scalar: "scalar" }, + ] } }, { scalar: value => { + trace.push(value); + if (value === "bad") throw failure; + return [value]; + } }, nullable); + const input = freeze({ values: ["first", "bad", "last"], after: "sibling" }); + expect(() => convert(input)).toThrow(failure); + expect(trace).toEqual(["first", "bad"]); + expect(input).toStrictEqual({ values: ["first", "bad", "last"], after: "sibling" }); + trace.length = 0; + expect(convert(freeze({ values: ["good"], after: "sibling" }))).toStrictEqual({ values: [["good"]], after: ["sibling"] }); + expect(trace).toEqual(["good", "sibling"]); + }); +}); diff --git a/packages/rescript-relay/__tests__/utils-tests.js b/packages/rescript-relay/__tests__/utils-tests.js index 8e5f0336..61065a78 100644 --- a/packages/rescript-relay/__tests__/utils-tests.js +++ b/packages/rescript-relay/__tests__/utils-tests.js @@ -85,13 +85,8 @@ describe("conversion", () => { undefined ); - try { - expect(JSON.stringify(traversedObj)).toBeDefined(); - } catch (e) { - console.error(e); - } - - expect.assertions(1); + expect(() => JSON.stringify(traversedObj)).not.toThrow(); + expect(traversedObj.fragmentRefs).not.toHaveProperty("fragmentRefs"); }); it("handles converting enums", () => { @@ -1017,12 +1012,7 @@ describe("conversion", () => { expect(spy).not.toHaveBeenCalled(); }); - test("[T]! / [T] — array contains a null element: CURRENT BEHAVIOR calls parse(null)", () => { - // Locks in current behavior. utils.js:109-113 maps `converter` - // over every element with no per-element null check, so a wire - // null reaches user code as `parse(null)`. A future fix should - // produce `[parse("a"), undefined, parse("b")]` — out of scope - // for this PR. + test("[T]! / [T] — nullable elements bypass parse", () => { expect( traverser( { xs: ["a", null, "b"] }, @@ -1030,7 +1020,7 @@ describe("conversion", () => { { [SCALAR]: parse }, undefined ) - ).toEqual({ xs: ["parsed:a", "parsed:null", "parsed:b"] }); + ).toEqual({ xs: ["parsed:a", undefined, "parsed:b"] }); }); test("BS_PRIVATE_NESTED_SOME_NONE on a ca field passes through unchanged", () => { @@ -1073,10 +1063,7 @@ describe("conversion", () => { expect(spy).not.toHaveBeenCalled(); }); - test("[T]! / [T] — array contains null element: CURRENT BEHAVIOR calls serialize(null)", () => { - // Symmetric to the read-side cell of the same name. Out of - // scope for this PR; correct output would be - // `[serialize(10), null, serialize(30)]`. + test("[T]! / [T] — nullable elements bypass serialize", () => { expect( traverser( { xs: [10, null, 30] }, @@ -1087,7 +1074,7 @@ describe("conversion", () => { ).toEqual({ xs: [ { serialized: 10 }, - { serialized: null }, + null, { serialized: 30 }, ], }); diff --git a/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.res b/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.res index e6bede53..791bb57d 100644 --- a/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.res +++ b/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.res @@ -13,17 +13,7 @@ module Internal = { @live type fragmentRaw @live - let fragmentConverter: dict>> = %raw( - json`{}` - ) - @live - let fragmentConverterMap = () - @live - let convertFragment = v => v->RescriptRelay.convertObj( - fragmentConverter, - fragmentConverterMap, - None - ) + let convertFragment = value => RescriptRelay.convertWithoutPlan(value, None) } type t diff --git a/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.res b/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.res index dd575f06..6c609e95 100644 --- a/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.res +++ b/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.res @@ -47,48 +47,54 @@ module Types = { type queryRef module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"inputC":{"recursiveC":{"r":"inputC"},"intStr":{"c":"TestsUtils.IntString"}},"__root":{"__relay_internal__pv__ProvidedVariablesIntStr":{"c":"TestsUtils.IntString"},"__relay_internal__pv__ProvidedVariablesInputCArr":{"r":"inputC"},"__relay_internal__pv__ProvidedVariablesInputC":{"r":"inputC"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["__relay_internal__pv__ProvidedVariablesInputC"],"reference":"inputC"},{"list":1,"path":["__relay_internal__pv__ProvidedVariablesInputCArr"],"reference":"inputC"},{"path":["__relay_internal__pv__ProvidedVariablesIntStr"],"scalar":"0"}],"inputC":[{"path":["intStr"],"scalar":"0"},{"path":["recursiveC"],"reference":"inputC"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.IntString": TestsUtils.IntString.serialize, + let variablesCallbacks = { + "0": TestsUtils.IntString.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) @live type wrapResponseRaw + %%private( @live - let wrapResponseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let wrapResponseConverter: JSON.t = %raw(json`{"roots":{"__root":[{"fragments":true,"path":["loggedInUser"]}]},"version":2}`) @live - let wrapResponseConverterMap = () + let wrapResponseCallbacks = () @live - let convertWrapResponse = v => v->RescriptRelay.convertObj( + let preparedWrapResponseConverter = RescriptRelay.prepareConversion( wrapResponseConverter, - wrapResponseConverterMap, + wrapResponseCallbacks, null ) + ) + @live + let convertWrapResponse = value => RescriptRelay.runConversion(preparedWrapResponseConverter, value) @live type responseRaw + %%private( @live - let responseConverter: dict>> = %raw( - json`{"__root":{"loggedInUser":{"f":""}}}` - ) + let responseConverter = wrapResponseConverter @live - let responseConverterMap = () + let responseCallbacks = () @live - let convertResponse = v => v->RescriptRelay.convertObj( + let preparedResponseConverter = RescriptRelay.prepareConversion( responseConverter, - responseConverterMap, + responseCallbacks, None ) + ) + @live + let convertResponse = value => RescriptRelay.runConversion(preparedResponseConverter, value) type wrapRawResponseRaw = wrapResponseRaw @live let convertWrapRawResponse = convertWrapResponse diff --git a/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.res b/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.res index a516d383..03d09104 100644 --- a/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.res +++ b/packages/rescript-relay/__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.res @@ -13,20 +13,22 @@ module Types = { } module Internal = { + %%private( @live - let variablesConverter: dict>> = %raw( - json`{"inputC":{"recursiveC":{"r":"inputC"},"intStr":{"c":"TestsUtils.IntString"}},"__root":{"__relay_internal__pv__ProvidedVariablesIntStr":{"c":"TestsUtils.IntString"},"__relay_internal__pv__ProvidedVariablesInputCArr":{"r":"inputC"},"__relay_internal__pv__ProvidedVariablesInputC":{"r":"inputC"}}}` - ) + let variablesConverter: JSON.t = %raw(json`{"roots":{"__root":[{"path":["__relay_internal__pv__ProvidedVariablesInputC"],"reference":"inputC"},{"list":1,"path":["__relay_internal__pv__ProvidedVariablesInputCArr"],"reference":"inputC"},{"path":["__relay_internal__pv__ProvidedVariablesIntStr"],"scalar":"0"}],"inputC":[{"path":["intStr"],"scalar":"0"},{"path":["recursiveC"],"reference":"inputC"}]},"version":2}`) @live - let variablesConverterMap = { - "TestsUtils.IntString": TestsUtils.IntString.serialize, + let variablesCallbacks = { + "0": TestsUtils.IntString.serialize, } @live - let convertVariables = v => v->RescriptRelay.convertObj( + let preparedVariablesConverter = RescriptRelay.prepareConversion( variablesConverter, - variablesConverterMap, + variablesCallbacks, None ) + ) + @live + let convertVariables = value => RescriptRelay.runConversion(preparedVariablesConverter, value) } module Utils = { @@warning("-33") diff --git a/packages/rescript-relay/benchmarks/README.md b/packages/rescript-relay/benchmarks/README.md new file mode 100644 index 00000000..6ff292ef --- /dev/null +++ b/packages/rescript-relay/benchmarks/README.md @@ -0,0 +1,116 @@ +# Response conversion performance + +Run from `packages/rescript-relay`: + +```sh +yarn test:all +yarn test:conversion:mutations +yarn bench:conversion:prepared --output /tmp/conversion-after.json +``` + +`test:all` compiles the checked-in generated Relay artifacts and ReScript bindings, +then runs the mounted React/Relay tests, the separate persisted-query suite, and +utility tests with coverage thresholds. Build the PPX first as in the bindings CI +workflow (`opam install . --deps-only`, then `opam exec -- dune build +bin/RescriptRelayPpxApp.exe` in `rescript-relay-ppx`). No Relay compiler regeneration +is needed unless GraphQL selections change. + +For a before/after comparison using the **same fixtures and harness**: + +```sh +git show 520cac4933c118a85cf0dffcd1a19a5dba34ad4f:packages/rescript-relay/src/utils.js > /tmp/utils-before.cjs +yarn bench:conversion --implementation /tmp/utils-before.cjs --output /tmp/conversion-before.json +yarn bench:conversion:prepared --compare /tmp/conversion-before.json --output /tmp/conversion-after.json +``` + +For paired comparisons of equivalent prepared runtimes, extract both JavaScript +modules from the baseline revision and alternate their execution within each +sample. For example, to reproduce the latest JavaScript optimization: + +```sh +mkdir -p /tmp/relay-conversion-before +git show 3de062b:packages/rescript-relay/src/utils.js > /tmp/relay-conversion-before/utils.js +git show 3de062b:packages/rescript-relay/src/prepareConversion.js > /tmp/relay-conversion-before/prepareConversion.js +node benchmarks/compare-conversion.js --baseline /tmp/relay-conversion-before/utils.js --output /tmp/paired-conversion.json +``` + +The paired harness checks complete output equality and input immutability, warms +both implementations, and collects 31 pairs with alternating execution order. +Reports retain every pair, source hashes, and host load. `--candidate ` can +select another runtime; `--legacy` compares the legacy API instead. It defaults +to prepared conversion and requires equivalent behavior from both implementations. +Historical runtimes with intentionally different semantics need the original +fixture-based harness above. Pairing reduces temporal bias, but does not remove +JIT or shared-host noise; also check separate processes and repeat measurements. + +Run those commands sequentially on an otherwise idle machine, with the same Node +version. Repeat in reversed order to check for warmup, CPU frequency, or load +bias. `--samples` (default 15) and `--duration` (default 40 ms per sample) control +measurement length. Each case calibrates and warms up before collecting samples; +reports include median, p10/p90, throughput, runtime, CPU, and fixture version. +Fixtures cover both response reads and recursive input writes. Correctness and +input immutability are asserted before timing; expected results are independent +of the instruction interpreter. Raw fragment refs are verified by the tests, +not by comparing to older implementations that incorrectly traversed them. + +`newOutputObjects` counts unique objects reachable from the output that were not +in the input, including arrays, dates, and fragment-ref snapshots. It is a stable +measure of output allocations, **not** total allocations, heap bytes, or peak +memory. It excludes temporary objects that are already unreachable. CI uploads +this report and conversion coverage; timings are informational, while correctness +and coverage failures block CI. + +For CPU and GC investigation, `--cpu-time` records CPU nanoseconds alongside +elapsed time and retains individual samples in the JSON report. It uses the +current thread's CPU time when Node provides it; older Node versions fall back to +process CPU time, including worker threads. The report identifies which was used. +CPU time helps diagnose scheduling waits but still varies with frequency, +contention, GC and JIT behavior. It does not replace elapsed-time measurements. + + +```sh +node benchmarks/conversion.js --prepared --cpu-time --output /tmp/conversion-cpu.json +node --cpu-prof --cpu-prof-dir=/tmp benchmarks/conversion.js +node --trace-gc benchmarks/conversion.js > /tmp/conversion-gc.log +``` + +Open the CPU profile in a compatible profiler. Profile baseline and candidate +separately; profiling changes timings. These are conversion microbenchmarks, +not end-to-end React latency measurements. The mounted tests exercise actual +compiled query/fragment hooks and store updates without relying on timing. + +## Implementation and compatibility + +New compiler artifacts use version 2 plans: lossless path segments, explicit list +depths, and named scalar/union/reference operations. `prepareConversion` validates +and prepares each plan at module initialization. The response path runs composed +record, list, scalar and union functions without creating paths or interpreting +opcodes. Preparation time is recorded separately as `preparationNs`; it is not +included in warm conversion timings. Prepared converters snapshot callback +bindings and plan metadata; callback state and response data are never cached. + +Lists and records use lazy copying and preserve unchanged branches. Scalar +outputs and JSON remain opaque. Both generated fragment-ref properties point to +one raw snapshot. Null list members bypass scalar callbacks. Union callbacks run +after member conversion for reads and before it for writes, including root unions. + +The legacy `traverser` remains available for existing generated artifacts and can +be measured with `yarn bench:conversion`. It uses string paths and retains the +old protocol's ambiguous underscore paths and inability to encode nested list +depth. Regenerate artifacts with the matching compiler to use prepared plans. +New generated artifacts require this runtime; old generated artifacts still work +with it. See [the conversion contract](../docs/conversion-contract.md) for exact +semantics, intentional bug fixes, supported inputs, and validation boundaries. + +The existing bindings already memoize conversions with `useMemo` keyed by the +Relay snapshot. The mounted regression tests verify unchanged rerenders reuse +that result and store updates produce freshly converted data. No hook-level +cache or dependency changes were needed. + +See [bundle-size measurements and budgets](SIZE.md) for complete generated JS, +shared/separate bundles, gzip/Brotli reports, and reproduction commands. + +A ReScript runtime port was evaluated but not adopted: it increased bundle size +and regressed some conversion workloads. The prototype and its measurements remain +available in [the experiment commit](https://github.com/zth/rescript-relay/tree/e1b12c732ea258f338defeed058061e8f78609e4/packages/rescript-relay/benchmarks/rescript-runtime). +The shipping runtime remains JavaScript. diff --git a/packages/rescript-relay/benchmarks/RESULTS.md b/packages/rescript-relay/benchmarks/RESULTS.md new file mode 100644 index 00000000..746cd85a --- /dev/null +++ b/packages/rescript-relay/benchmarks/RESULTS.md @@ -0,0 +1,110 @@ +# Prepared conversion benchmark results + +Measured on v24.16.0, AMD RYZEN AI MAX+ 395 w/ Radeon 8060S. Each implementation ran sequentially in forward order, then reverse order; 15 samples per fixture, targeting 40 ms each. Ratios below span those two runs; these are local conversion microbenchmarks, not application latency improvements. + +| Case | Prepared median (µs) | vs master | vs #673 | vs first optimization | +|---|---:|---:|---:|---:| +| small fragment | 0.112–0.121 | 11.51–11.58× | 3.10–3.27× | 2.09–2.33× | +| unchanged object | 0.059–0.062 | 12.80–13.24× | 3.49–3.52× | 1.75–1.97× | +| nullable query | 0.148–0.153 | 13.72–14.10× | 4.13–4.50× | 2.47–2.65× | +| connection / 100 rows | 20.724–24.012 | 30.91–35.23× | 11.43–13.73× | 2.66–3.06× | +| plural fragment / 100 rows | 15.192–16.458 | 8.62–9.50× | 2.23–2.88× | 1.64–2.15× | +| union list / 100 rows | 18.347–18.512 | 12.59–12.62× | 4.43–4.61× | 3.14–3.34× | +| custom scalar list | 12.116–12.560 | 0.88–0.94× | 0.89–0.94× | 0.89–0.92× | +| opaque JSON list | 0.447–0.460 | 1.22–1.24× | 1.47–1.48× | 0.76–0.78× | +| recursive inputs | 16.373–18.089 | 19.19–20.18× | 6.43–7.95× | 1.49–1.81× | + +Ratios above 1 mean prepared conversion was faster. Date construction dominates the custom-scalar fixture: prepared conversion is slightly slower there. Opaque JSON lists are faster than master/#673 but slower than the first legacy optimization. The new representation fixes ambiguous paths and nested-list semantics in addition to avoiding per-response instruction lookup. + +Preparation is outside the timed response path; per-fixture `preparationNs` is reported separately in each prepared JSON file. It is an average over 1,000 preparations, not a startup-latency measurement. `newOutputObjects` counts reachable output objects, not total temporary allocations or retained memory. + +Sources: + +- `master`: `520cac4933c118a85cf0dffcd1a19a5dba34ad4f`, extracted `src/utils.js`. +- `pr-673`: `c451c1afb3193550586f80b36b46b84c8965d355`, extracted `src/utils.js`. +- `first-pass`: `3770abc`, extracted `src/utils.js`. +- `legacy` / `prepared`: the prepared runtime at `e856b90`, before the later bundle-size optimization. + +For the historical table above, use the fixture-version 2 reports named `forward-*.json` and `reverse-*.json` for comparisons. Earlier `baseline.json`, `optimized.json`, and `pr-673.json` are historical fixture-version 1 measurements and must not be mixed with them. See [the benchmark guide](README.md) for commands. + +## Further JavaScript specialization (fixture version 3) + +The JavaScript runtime now has two narrow fast paths: generic records bypass +empty instruction-dictionary lookups, and opaque lists normalize nullability +without per-element converter calls. The list implementation is selected while +preparing the plan; ordinary list conversion is unchanged. Sparse-array handling, +metadata boundaries, callback ordering, lazy copying and output identities retain +their existing semantics. More aggressive wrapper/inlining changes were not kept. + +The baseline is `3de062b`, immediately before this optimization. Two final paired +runs used Node 24.16.0, 31 alternating pairs per case, and CPU affinity 7 on the +same AMD host. Negative values mean less conversion time; these are ranges of +median per-pair time changes, not application latency gains: + +| Case | Paired conversion-time change | +| --- | ---: | +| Small fragment | -9.0% to -8.9% | +| Unchanged object | -15.0% to -14.8% | +| Nullable query | -19.1% to -18.9% | +| Connection, 100 rows | -20.2% to -19.8% | +| Plural fragment, 100 rows | -15.0% to -14.5% | +| Union list, 100 rows | -25.1% to -17.7% | +| Custom scalar list | -2.3% to -0.1% (approximately unchanged) | +| Opaque JSON list | -66.8% to -66.3% (about 3× throughput) | +| Recursive inputs | -11.1% to -5.7% | + +Separate-process measurements remained inconsistent, including slower results +for some cases, even with CPU affinity and CPU-time diagnostics. The shared host, +JIT warmup and workload mix limit the confidence of those comparisons. The paired +runs are the evidence for the gains above; they do not establish a universal +speedup across JS engines or application workloads. Confirm on an idle target +machine before using the percentages as release guarantees. Reachable output +object counts are unchanged in all nine fixtures. + +[Recorded results](results/js-specialization/) include both paired summaries, +individual samples from the second run, source hashes, separate-process wall/CPU +measurements, and bundle sizes. Reproduce with `compare-conversion.js` as described +in [README.md](README.md); on Linux, prefix commands with `taskset -c ` to hold +CPU affinity constant. Default affinity is unrestricted. Do not combine these +ratios with the older fixture-version 2 table above. + +Validation: **249 existing/new JS tests pass** (248 main, one persisted-query), +including 145 utility tests; both runtime modules retain **100% statement, branch, +function and line coverage**. All **18 targeted mutation checks**, native +ESM/CommonJS parity, and artifact size budgets pass. The six new tests exercise +opaque-list nulls/holes/inherited elements/cyclic values and generic-object +metadata, inherited keys, own-key order and identity preservation in both nullable +directions. Coverage and mutations provide evidence, not exhaustive correctness. + +## Final result against the current default branch + +Verified that the remote default branch is still `520cac4933c118a85cf0dffcd1a19a5dba34ad4f`. +Fresh runs compare its legacy runtime directly with the final prepared runtime at +`e1b12c7`, using the same version 3 fixtures, Node 24.16.0, 9 samples per case and +25 ms calibration targets. Run order was baseline/final, then final/baseline; +CPU affinity was unrestricted. These are measured microbenchmark ranges, not +application-wide latency claims or products of earlier benchmark ratios. + +| Case | Final speedup over current default branch | +| --- | ---: | +| Small fragment | 13.4–14.3× | +| Unchanged object | 17.9–18.9× | +| Nullable query | 17.0–17.1× | +| Connection, 100 rows | 41.2–45.3× | +| Plural fragment, 100 rows | 11.5–11.7× | +| Union list, 100 rows | 12.9–14.4× | +| Opaque JSON list | 3.6–3.7× | +| Recursive inputs | 19.2–23.0× | +| Custom scalar list | 5–7% more time (slower) | + +Raw reports are `results/js-specialization/main-comparison-*.json`. +The current machine remains noisy; treat these as approximate workload-specific +results. The scalar fixture is dominated by Date construction. + +For the same 129 generated artifacts, combined gzip decreased from 37,573 to +36,319 bytes (-1,254). The complete CommonJS-entry runtime increased from 1,144 +to 2,409 gzip bytes (+1,265). Adding these separately compressed components gives +38,717 → 38,728 bytes: effectively size-neutral for this fixture set. This is not +a measurement of a single compressed application bundle; actual results depend +on artifact count, splitting, imports and compression. See `SIZE.md` for separate +artifact bundles and native ESM runtime measurements. diff --git a/packages/rescript-relay/benchmarks/SIZE.md b/packages/rescript-relay/benchmarks/SIZE.md new file mode 100644 index 00000000..f18fb3e2 --- /dev/null +++ b/packages/rescript-relay/benchmarks/SIZE.md @@ -0,0 +1,174 @@ +# Generated conversion bundle size + +This measures **complete compiled JavaScript artifacts**, including Relay nodes, +conversion metadata, helper functions, and initialization calls. It replaces the +earlier metadata-only gzip estimate. esbuild 0.25.12 bundles/minifies a fixed set +of 129 pre-existing artifacts; gzip uses level 9 and Brotli uses quality 11. + +Dependencies (React, Relay, bindings, custom scalar modules, and generated imports) +are external in every measurement. These are artifact-only bundles, **not a +production application build**. The combined case exports all artifacts in one +bundle. The separate case sums independently bundled/compressed artifacts. Real +code splitting and import usage will change the result. The shared runtime is +measured separately below and must not be counted once per artifact. + +| Version | One bundle, minified | One bundle, gzip | One bundle, Brotli | Separate bundles, gzip | Separate bundles, Brotli | +|---|---:|---:|---:|---:|---:| +| Original (`520cac4`) | 284,957 | 37,573 | 26,976 | 119,093 | 106,076 | +| First prepared redesign (`e856b90`) | 313,772 | 41,628 | 29,248 | 126,052 | 112,258 | +| After size optimization | 277,982 | 36,961 | 26,651 | 119,154 | 105,719 | +| Compact callback IDs | 274,404 | 36,667 | 26,495 | 118,974 | 105,519 | +| Pruned plain-list hints | 272,639 | 36,319 | 26,233 | 118,381 | 104,973 | + +All numbers are bytes. The first size optimization (before compact callback IDs) saves 35,790 minified +bytes / 4,667 gzip bytes in the combined bundle, and 6,898 gzip bytes across the +separate bundles. Relative to the original, combined gzip is 612 bytes smaller; +separate gzip is 61 bytes larger. These totals do not promise that each individual +artifact shrinks; per-artifact sizes are included in the JSON reports. + +## Changes + +- Keep plans, callback maps, and prepared handles private, so the compiler does + not export them as properties of each `Internal` object. Conversion functions + remain available to the bindings. +- Share identical immutable plans within an artifact, including read/write + directions. Their callback maps and prepared converters remain independent. +- Emit a call to a shared generic converter when no instructions are needed. + This avoids allocating empty plans and preparing equivalent converters in each + generated module. The shared converter retains no application data or callbacks. + +The lossless, readable version 2 instruction format is unchanged. No minified +opcodes, dynamic code generation, global plan registry, or input memoization was +introduced. Nonempty prepared converters execute the same response traversal. + +## Reproduction and CI + +After building the PPX and compiling the bindings (`yarn build`), run: + +```sh +yarn size:conversion --check --output /tmp/conversion-size.json +``` + +To compare a compiled package in another checkout: + +```sh +yarn size:conversion --root /path/to/other/packages/rescript-relay --output /tmp/other-size.json +``` + +Both packages must use the same ReScript version and CommonJS output configuration. +For these reports, original artifacts came from `520cac4933c118a85cf0dffcd1a19a5dba34ad4f` +and the earlier redesign from `e856b90`; both were compiled with ReScript 12.3.0. +The measurement deliberately externalizes dependencies so the comparison isolates +compiler output. `size-fixtures.json` pins the identical set of selections, excluding +the new regression query and schema helper. Use separate worktrees or scratch +packages when compiling historical artifacts; do not overwrite current artifacts. + +Bindings CI checks minified, gzip, and Brotli budgets for both bundle layouts and +uploads the report. Budgets now allow 1% headroom over the pruned-list result. Intentional changes +to fixture coverage, esbuild version, or budgets need review. The raw reports are +`results/size-original.json`, `results/size-before.json`, and `results/size-current.json`. + +## Shared runtime + +This conservatively includes both prepared conversion and the legacy compatibility +export, bundled once. The original runtime is extracted from `520cac4`; the previous +prepared runtime is extracted from `e856b90`. + +| Runtime | Minified | Gzip | Brotli | +|---|---:|---:|---:| +| original | 2916 | 1144 | 1010 | +| before | 5605 | 2261 | 2041 | +| current | 5786 | 2330 | 2108 | + +The runtime remains larger than the original because it supports both protocols. +Shared nullable converters add a small fixed cost while removing per-artifact +initialization code. There is no measured full-app bundle result yet. + +## Performance recheck + +`size-perf-{forward,reverse}-{before,current}.json` records the post-change +performance check against `e856b90`, using the same version 3 harness for both. +Version 3 models the compiler's shared empty-plan calls as well as prepared plans; +it reports zero per-artifact preparation time when the shared converter is used. +Do not compare these reports to older fixture-version 2 reports with `--compare`. + +This run was noisy (host load averaged about 32 runnable tasks); several apparent +speedups reversed when the run order reversed. It does **not** establish an +additional speedup or a reliable regression estimate. Nonempty plans use unchanged +response traversal code; empty plans reuse that same generic nullable traversal +through a shared entry point. Unit, independent-model, mounted, and mutation tests +pass. A quieter application benchmark is still needed for precise timing deltas. + +## Compact callback IDs + +The compiler now gives each scalar/union callback a stable, local string ID: + +```js +// Before +{path: ["createdAt"], scalar: "TestsUtils.Datetime"} +// After +{path: ["createdAt"], scalar: "0"} +// The generated callback table still names the function: +{"0": TestsUtils.Datetime.parse} +``` + +Only callback identifiers change. The runtime already resolves string keys in the +callback table during preparation, so there is no runtime change or decoder cost. +Paths, explicit list depth, operation names, and named input references remain +readable and lossless. Scalar/union callbacks occupy distinct slots even if their +names coincide; sorting and deduplication make IDs independent of instruction order. +Slots are scoped to each conversion context, so sharing a plan never shares its +callback bindings between read/write directions or between modules. + +Across the fixed fixture set, literal instruction JSON shrinks from 12,378 to +10,936 bytes (11.6%). Complete generated bundles shrink by another 3,578 minified +bytes, 294 gzip bytes combined, or 180 gzip bytes across separate bundles, versus +`f366d16`. Gzip savings are modest because repeated callback names already compress +well. `results/size-callback-slots.json` is the new full-artifact measurement. + +Experiments with escaped string-path maps and abbreviated operation keys saved +only 77–150 bytes in the combined gzip bundle before decoder costs; they were not +adopted. Indexed callback arrays also produced a worse combined gzip result than +keyed callback tables. The named version 2 protocol remains unchanged. + +## Final pass: omit redundant native-list hints + +A list instruction is unnecessary when there is no scalar/union/reference/opaque/ +fragment operation at or below its path. The generic converter already traverses +plain lists and normalizes nullability. The compiler now omits those hints. When +this leaves an empty root plan, it emits the existing shared-converter call and +omits the per-artifact plan and prepared handle as well. + +Required list depths are retained, including ancestors of special conversions; +path comparisons use full segments, so `a_b` cannot be confused with `a.b`. Named +input references remain explicit even when their target only needs normalization, +because they can also unwrap input unions. Neither the protocol nor runtime code +changes. + +Relative to compact callback IDs (`3a55220`), this saves another **1,765 minified +bytes / 348 gzip bytes combined**, or **593 gzip bytes across separate bundles**. +The final fixture bundles are smaller than original master by 1,254 gzip bytes +combined and 712 gzip bytes separately, excluding the shared runtime and external +dependencies as before. `results/size-pruned-lists.json` records the complete result. + +Compiler tests cover every retained operation boundary and omission of irrelevant +siblings/ancestors. JS tests compare omitted versus explicit native-list hints to +an independent evaluator at depths 1–3 in both nullable directions, including +sparse arrays, option markers, and falsey values. The full mounted suite also uses +the regenerated artifacts. More elaborate format compression was intentionally +left out. + +## JavaScript hot-path specialization + +Generic records and opaque lists now use dedicated loops. This adds **79 gzip +bytes** to the complete CommonJS-entry runtime bundle (2,330 → 2,409), or **82 +bytes** to the equivalent native ESM bundle (2,238 → 2,320). Both measurements use +esbuild 0.25.12, `bundle: true`, `minify: true`, `format: "esm"`, gzip level 9, and +retain all four public exports. CommonJS module wrappers explain the different +absolute sizes. Minified/Brotli details are in +[`results/js-specialization`](results/js-specialization/). + +The 129-artifact generated-code bundles and their budgets are unchanged. This is +a shared runtime cost, not an additional cost per generated fragment or query. +See [the performance results](RESULTS.md#further-javascript-specialization-fixture-version-3) +for measurements and their limits. diff --git a/packages/rescript-relay/benchmarks/compare-conversion.js b/packages/rescript-relay/benchmarks/compare-conversion.js new file mode 100644 index 00000000..51d46148 --- /dev/null +++ b/packages/rescript-relay/benchmarks/compare-conversion.js @@ -0,0 +1,110 @@ +#!/usr/bin/env node +// Alternate order within each pair to reduce temporal bias. These are warm +// microbenchmarks, not end-to-end latency or proof of parity across JS engines. +const {performance} = require("perf_hooks"); +const fs = require("fs"); +const assert = require("assert/strict"); +const os = require("os"); +const crypto = require("crypto"); +const v8 = require("v8"); +const path = require("path"); +const {cases, converters} = require("./fixtures"); +const args = process.argv.slice(2); +const option = name => { + const index = args.indexOf(name); + return index < 0 ? undefined : args[index + 1]; +}; +if (!option("--baseline") || !option("--output")) { + throw new Error("Usage: compare-conversion.js --baseline --output [--candidate ] [--legacy]"); +} +const baselinePath = path.resolve(option("--baseline")); +const candidatePath = option("--candidate") ? path.resolve(option("--candidate")) : require.resolve("../src/utils"); +const baseline = require(baselinePath); +const candidate = require(candidatePath); +const samples = 31; +const targetSampleMs = 20; +let sink; +function batch(convert, root, count) { + const start = performance.now(); + for (let i = 0; i < count; i++) sink = convert(root); + return (performance.now() - start) * 1e6 / count; +} +function median(values) { + return [...values].sort((a, b) => a - b)[Math.floor(values.length / 2)]; +} +function hash(filename) { + return crypto.createHash("sha256") + .update(fs.readFileSync(require.resolve(filename))).digest("hex"); +} +function build(runtime, fixture, mode) { + if (mode === "legacy") { + return value => runtime.traverser(value, fixture.maps, converters, fixture.nullable); + } + if (Object.keys(fixture.plan.roots).length === 1 && fixture.plan.roots.__root.length === 0) { + return value => runtime.convertWithoutPlan(value, fixture.nullable); + } + return runtime.prepareConversion(fixture.plan, converters, fixture.nullable); +} +const initialLoad = os.loadavg(); +const results = []; +for (const mode of [args.includes("--legacy") ? "legacy" : "prepared"]) { + for (const fixture of cases) { + const before = v8.deserialize(v8.serialize(fixture.root)); + const original = build(baseline, fixture, mode); + const ported = build(candidate, fixture, mode); + assert.deepEqual(ported(fixture.root), original(fixture.root), fixture.name); + let count = 100; + while (Math.min(batch(original, fixture.root, count), batch(ported, fixture.root, count)) * count < targetSampleMs * 1e6) { + count *= 2; + } + const measurements = []; + for (let i = 0; i < samples; i++) { + let baselineNs, candidateNs; + if (i % 2) { + candidateNs = batch(ported, fixture.root, count); + baselineNs = batch(original, fixture.root, count); + } else { + baselineNs = batch(original, fixture.root, count); + candidateNs = batch(ported, fixture.root, count); + } + measurements.push({baselineNs, candidateNs}); + } + assert.deepEqual(fixture.root, before, fixture.name + ": input mutated"); + const result = { + mode, + name: fixture.name, + count, + baselineNs: median(measurements.map(sample => sample.baselineNs)), + candidateNs: median(measurements.map(sample => sample.candidateNs)), + ratio: median(measurements.map(sample => sample.candidateNs / sample.baselineNs)), + samples: measurements, + }; + results.push(result); + console.log(mode, fixture.name, Math.round(result.baselineNs), Math.round(result.candidateNs), (result.ratio * 100 - 100).toFixed(1) + "%"); + } +} +function preparedHash(entry) { + const filename = path.join(path.dirname(entry), "prepareConversion.js"); + return fs.existsSync(filename) ? hash(filename) : null; +} +const report = { + fixtureVersion: 3, + node: process.version, + cpu: os.cpus()[0].model, + cpuAffinity: os.platform() === "linux" ? fs.readFileSync("/proc/self/status", "utf8").match(/^Cpus_allowed_list:\s*(.+)$/m)[1] : null, + samples, + targetSampleMs, + initialLoad, + finalLoad: os.loadavg(), + implementations: {baseline: baselinePath, candidate: candidatePath}, + sha256: { + baseline: hash(baselinePath), + preparedBaseline: preparedHash(baselinePath), + candidate: hash(candidatePath), + preparedCandidate: preparedHash(candidatePath), + fixtures: hash("./fixtures"), + }, + results, +}; +fs.writeFileSync(option("--output"), JSON.stringify(report, null, 2) + "\n"); +if (!sink) throw new Error("Missing benchmark result"); diff --git a/packages/rescript-relay/benchmarks/conversion.js b/packages/rescript-relay/benchmarks/conversion.js new file mode 100644 index 00000000..1df32b94 --- /dev/null +++ b/packages/rescript-relay/benchmarks/conversion.js @@ -0,0 +1,167 @@ +#!/usr/bin/env node +const { performance } = require("perf_hooks"); +const os = require("os"); +const path = require("path"); +const fs = require("fs"); +const assert = require("assert/strict"); +const v8 = require("v8"); +const { cases, converters } = require("./fixtures"); +const args = process.argv.slice(2); +const option = (name) => { + const i = args.indexOf(name); + return i < 0 ? undefined : args[i + 1]; +}; +const implementation = option("--implementation"); +const { traverser, prepareConversion, convertWithoutPlan } = require( + implementation ? path.resolve(implementation) : "../src/utils", +); +const samples = Number(option("--samples") || 15); +const duration = Number(option("--duration") || 40); +if ( + !Number.isInteger(samples) || + samples < 3 || + !Number.isFinite(duration) || + duration <= 0 +) { + throw new Error( + "Use --samples >= 3 and --duration > 0 (milliseconds per sample).", + ); +} +function dataOnly(value) { + if (Array.isArray(value)) return value.map(dataOnly); + if (!value || typeof value !== "object" || value instanceof Date) + return value; + return Object.fromEntries( + Object.entries(value) + .filter( + ([key]) => key !== "fragmentRefs" && key !== "updatableFragmentRefs", + ) + .map(([key, v]) => [key, dataOnly(v)]), + ); +} +function objects(value, seen = new Set()) { + if (value && typeof value === "object" && !seen.has(value)) { + seen.add(value); + Object.values(value).forEach((v) => objects(v, seen)); + } + return seen; +} +const prepared = args.includes("--prepared"); +if (prepared && !prepareConversion) + throw new Error("Implementation has no prepared converter"); +let sink; +const cpuTiming = args.includes("--cpu-time") + ? typeof process.threadCpuUsage === "function" ? "thread" : "process" + : undefined; +const readCpu = cpuTiming === "thread" + ? process.threadCpuUsage.bind(process) + : cpuTiming === "process" ? process.cpuUsage.bind(process) : undefined; +let lastCpuNs; +function batch(fixture, count) { + const cpuStart = readCpu && readCpu(); + const start = performance.now(); + for (let i = 0; i < count; i++) sink = fixture.convert(fixture.root); + const elapsed = performance.now() - start; + if (readCpu) { + const cpu = readCpu(cpuStart); + lastCpuNs = (cpu.user + cpu.system) * 1000; + } + return elapsed; +} +const results = cases.map((fixture) => { + const before = v8.deserialize(v8.serialize(fixture.root)); + // Match generated artifacts: empty plans reuse the shared nullable converter. + const withoutPlan = + prepared && + convertWithoutPlan && + Object.keys(fixture.plan.roots).length === 1 && + fixture.plan.roots.__root.length === 0; + let preparationNs = withoutPlan ? 0 : undefined; + if (prepared && !withoutPlan) { + const start = performance.now(); + for (let i = 0; i < 1000; i++) + sink = prepareConversion(fixture.plan, converters, fixture.nullable); + preparationNs = ((performance.now() - start) * 1e6) / 1000; + } + fixture.convert = withoutPlan + ? (value) => convertWithoutPlan(value, fixture.nullable) + : prepared + ? prepareConversion(fixture.plan, converters, fixture.nullable) + : (value) => traverser(value, fixture.maps, converters, fixture.nullable); + const converted = fixture.convert(fixture.root); + assert.deepStrictEqual(dataOnly(converted), fixture.expected, fixture.name); + assert.deepStrictEqual( + fixture.root, + before, + fixture.name + ": input mutated", + ); + const inputObjects = objects(fixture.root); + const newOutputObjects = [...objects(converted)].filter( + (v) => !inputObjects.has(v), + ).length; + // Calibrate outside the measured samples; warm the same call site first. + let iterations = 100; + while (batch(fixture, iterations) < duration) iterations *= 2; + const cpuSamples = []; + const values = Array.from({ length: samples }, () => { + const wallNs = (batch(fixture, iterations) * 1e6) / iterations; + if (readCpu) cpuSamples.push({ wallNs, cpuNs: lastCpuNs / iterations }); + return wallNs; + }).sort((a, b) => a - b); + const cpuValues = cpuSamples.map(sample => sample.cpuNs).sort((a, b) => a - b); + const median = values[Math.floor(values.length / 2)]; + return { + name: fixture.name, + iterations, + newOutputObjects, + preparationNs, + medianNs: median, + medianCpuNs: readCpu ? cpuValues[Math.floor(cpuValues.length / 2)] : undefined, + cpuSamples: readCpu ? cpuSamples : undefined, + p10Ns: values[Math.floor(values.length * 0.1)], + p90Ns: values[Math.floor(values.length * 0.9)], + opsPerSecond: 1e9 / median, + }; +}); +const report = { + fixtureVersion: 3, + mode: prepared ? "prepared" : "legacy", + node: process.version, + platform: `${os.platform()} ${os.arch()}`, + cpu: os.cpus()[0].model, + samples, + durationMs: duration, + cpuTiming, + implementation: implementation || "src/utils.js", + results, +}; +console.table( + results.map((r) => ({ + case: r.name, + "median ns": Math.round(r.medianNs), + ...(readCpu ? { "CPU ns": Math.round(r.medianCpuNs) } : {}), + "p10 ns": Math.round(r.p10Ns), + "p90 ns": Math.round(r.p90Ns), + "ops/sec": Math.round(r.opsPerSecond), + "new output objects": r.newOutputObjects, + })), +); +const baseline = option("--compare"); +if (baseline) { + const previous = JSON.parse(fs.readFileSync(baseline, "utf8")); + if (previous.fixtureVersion !== report.fixtureVersion) + throw new Error("Benchmark fixture versions differ"); + console.table( + results.map((r) => { + const old = previous.results.find((x) => x.name === r.name); + return { + case: r.name, + speedup: old ? (old.medianNs / r.medianNs).toFixed(2) + "x" : "n/a", + }; + }), + ); +} +if (option("--output")) + fs.writeFileSync(option("--output"), JSON.stringify(report, null, 2) + "\n"); +// Keep the observable result alive through the timed loops. +if (!sink) throw new Error("Missing benchmark result"); diff --git a/packages/rescript-relay/benchmarks/fixtures.js b/packages/rescript-relay/benchmarks/fixtures.js new file mode 100644 index 00000000..79721ce8 --- /dev/null +++ b/packages/rescript-relay/benchmarks/fixtures.js @@ -0,0 +1,164 @@ +// Plain fixtures keep this benchmark independent of compilation and React. +const rows = Array.from({ length: 100 }, (_, i) => ({ + id: String(i), + name: "User " + i, + status: i % 2 ? "ONLINE" : "OFFLINE", + avatar: null, + profile: { bio: null, score: i }, +})); +const converters = { + enum: (v) => v.toLowerCase(), + scalar: (v) => new Date(v), + union: (v) => v, +}; +const cases = [ + { + name: "small fragment", + root: rows[0], + maps: { __root: { "": { f: "" }, status: { e: "enum" } } }, + }, + { + name: "unchanged object", + root: { id: "1", name: "User", profile: { score: 42 } }, + maps: {}, + }, + { + name: "nullable query", + root: { viewer: rows[0], missing: null }, + maps: {}, + }, + { + name: "connection / 100 rows", + root: { users: { edges: rows.map((node) => ({ cursor: node.id, node })) } }, + maps: { + __root: { + users_edges_node: { f: "" }, + users_edges_node_status: { e: "enum" }, + }, + }, + }, + { + name: "plural fragment / 100 rows", + root: rows, + maps: { __root: { "": { f: "" }, status: { e: "enum" } } }, + }, + { + name: "union list / 100 rows", + root: { members: rows.map((v) => ({ ...v, __typename: "User" })) }, + maps: { + __root: { + members: { u: "union" }, + members_User: { f: "" }, + members_User_status: { e: "enum" }, + }, + }, + }, + { + name: "custom scalar list", + root: { dates: Array(100).fill("2025-01-01T00:00:00Z"), after: null }, + maps: { __root: { dates: { ca: "scalar" } } }, + }, + { + name: "opaque JSON list", + root: { items: rows }, + maps: { __root: { items: { b: "a" } } }, + }, + { + name: "recursive inputs", + root: { input: { status: "ONLINE", children: rows } }, + maps: { + __root: { input: { r: "Input" } }, + Input: { status: { e: "enum" }, children: { r: "Input" } }, + }, + nullable: null, + }, +]; +module.exports = { cases, converters }; + +// Expected data is independent of the instruction interpreter. Fragment refs are +// checked separately in unit/mounted tests (older releases rewrote nested refs). +const convertedRow = (row, nullable, enumValue) => ({ + ...row, + avatar: nullable, + status: enumValue ? row.status.toLowerCase() : row.status, + profile: { ...row.profile, bio: nullable }, +}); +const expected = [ + convertedRow(rows[0], undefined, true), + cases[1].root, + { viewer: convertedRow(rows[0], undefined, false), missing: undefined }, + { + users: { + edges: rows.map((row) => ({ + cursor: row.id, + node: convertedRow(row, undefined, true), + })), + }, + }, + rows.map((row) => convertedRow(row, undefined, true)), + { + members: rows.map((row) => ({ + ...convertedRow(row, undefined, true), + __typename: "User", + })), + }, + { + dates: Array.from({ length: 100 }, () => new Date("2025-01-01T00:00:00Z")), + after: undefined, + }, + { items: rows }, + { + input: { + status: "online", + children: rows.map((row) => convertedRow(row, null, true)), + }, + }, +]; +cases.forEach((fixture, i) => { + fixture.expected = expected[i]; +}); + +// Version 2 plans for the same workloads, with explicit list depth and segments. +const field = (path, instruction) => ({ path, ...instruction }); +const plans = [ + { + __root: [ + field([], { fragments: true }), + field(["status"], { scalar: "enum" }), + ], + }, + { __root: [] }, + { __root: [] }, + { + __root: [ + field(["users", "edges"], { list: 1 }), + field(["users", "edges", "node"], { fragments: true }), + field(["users", "edges", "node", "status"], { scalar: "enum" }), + ], + }, + { + __root: [ + field([], { fragments: true }), + field(["status"], { scalar: "enum" }), + ], + }, + { + __root: [ + field(["members"], { list: 1, union: "union" }), + field(["members", "User"], { fragments: true }), + field(["members", "User", "status"], { scalar: "enum" }), + ], + }, + { __root: [field(["dates"], { list: 1, scalar: "scalar" })] }, + { __root: [field(["items"], { list: 1, opaque: true })] }, + { + __root: [field(["input"], { reference: "Input" })], + Input: [ + field(["status"], { scalar: "enum" }), + field(["children"], { list: 1, reference: "Input" }), + ], + }, +]; +cases.forEach((fixture, i) => { + fixture.plan = { version: 2, roots: plans[i] }; +}); diff --git a/packages/rescript-relay/benchmarks/results/README.md b/packages/rescript-relay/benchmarks/results/README.md new file mode 100644 index 00000000..eb733a39 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/README.md @@ -0,0 +1,27 @@ +# Recorded local comparison + +Baseline: `520cac4933c118a85cf0dffcd1a19a5dba34ad4f` (latest default branch when work began). +Candidate: the accompanying `src/utils.js` changes. Recorded 2026-09-21. + +Both runs used v24.16.0, linux x64, AMD RYZEN AI MAX+ 395 w/ Radeon 8060S, 15 samples per case, +with a 40 ms calibration target. These runs were sequential, candidate +first, and reproduced the earlier baseline-first comparison. Times vary by +runtime and machine; these are local microbenchmark results, not guarantees. + +| Case | Baseline median | Optimized median | Speedup | New output objects (before → after) | +|---|---:|---:|---:|---:| +| small fragment | 1.39 µs | 0.26 µs | 5.36× | 3 → 3 | +| unchanged object | 0.79 µs | 0.11 µs | 7.35× | 1 → 0 | +| nullable query | 2.15 µs | 0.43 µs | 5.03× | 3 → 3 | +| connection / 100 rows | 787.34 µs | 67.43 µs | 11.68× | 703 → 403 | +| plural fragment / 100 rows | 141.25 µs | 30.76 µs | 4.59× | 301 → 301 | +| union list / 100 rows | 229.87 µs | 62.56 µs | 3.67× | 302 → 302 | +| custom scalar list | 12.47 µs | 11.25 µs | 1.11× | 102 → 102 | +| opaque JSON list | 0.60 µs | 0.35 µs | 1.72× | 2 → 0 | +| recursive inputs | 359.82 µs | 30.62 µs | 11.75× | 203 → 103 | + +Raw reports include p10/p90 spreads. See [benchmark instructions](../README.md) +for reproduction and the limits of the output-object count. Date construction +dominates the custom-scalar list case; its small timing difference is within the +observed noise. CPU profiling confirmed traversal, field dispatch, fragment +snapshot copying, and scalar callbacks as the remaining work. diff --git a/packages/rescript-relay/benchmarks/results/baseline.json b/packages/rescript-relay/benchmarks/results/baseline.json new file mode 100644 index 00000000..724e5fbc --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/baseline.json @@ -0,0 +1,94 @@ +{ + "fixtureVersion": 1, + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "baseline src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 51200, + "newOutputObjects": 3, + "medianNs": 1385.9065429687512, + "p10Ns": 1287.5365039062503, + "p90Ns": 1633.2075390625, + "opsPerSecond": 721549.3750811649 + }, + { + "name": "unchanged object", + "iterations": 51200, + "newOutputObjects": 1, + "medianNs": 786.9883593750026, + "p10Ns": 746.9491992187471, + "p90Ns": 873.8155468750009, + "opsPerSecond": 1270666.8251029323 + }, + { + "name": "nullable query", + "iterations": 25600, + "newOutputObjects": 3, + "medianNs": 2151.13203125, + "p10Ns": 2052.9993359375, + "p90Ns": 2333.326679687495, + "opsPerSecond": 464871.51205633365 + }, + { + "name": "connection / 100 rows", + "iterations": 100, + "newOutputObjects": 703, + "medianNs": 787343.75, + "p10Ns": 757014.3700000016, + "p90Ns": 847085.5799999981, + "opsPerSecond": 1270.0932724746974 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 400, + "newOutputObjects": 301, + "medianNs": 141254.90249999985, + "p10Ns": 136209.04000000108, + "p90Ns": 151119.6025000004, + "opsPerSecond": 7079.4003061238955 + }, + { + "name": "union list / 100 rows", + "iterations": 200, + "newOutputObjects": 302, + "medianNs": 229873.82499999966, + "p10Ns": 222922.87500000384, + "p90Ns": 255679.87999999788, + "opsPerSecond": 4350.212556823299 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "medianNs": 12473.766562499974, + "p10Ns": 11483.63421875004, + "p90Ns": 13477.948750000054, + "opsPerSecond": 80168.24709597431 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 2, + "medianNs": 600.0888574218699, + "p10Ns": 566.5839941406325, + "p90Ns": 647.8469628906237, + "opsPerSecond": 1666419.8770432887 + }, + { + "name": "recursive inputs", + "iterations": 100, + "newOutputObjects": 203, + "medianNs": 359820.4699999951, + "p10Ns": 337718.6000000074, + "p90Ns": 382805.19999998435, + "opsPerSecond": 2779.163731290812 + } + ], + "baseRevision": "520cac4933c118a85cf0dffcd1a19a5dba34ad4f", + "recordedDate": "2026-09-21" +} diff --git a/packages/rescript-relay/benchmarks/results/forward-first-pass.json b/packages/rescript-relay/benchmarks/results/forward-first-pass.json new file mode 100644 index 00000000..3c13bc2f --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/forward-first-pass.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 2, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "/tmp/relay-conversion-audit/first-pass-utils.cjs", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "medianNs": 261.14952636718755, + "p10Ns": 239.84318359375007, + "p90Ns": 284.6326367187499, + "opsPerSecond": 3829223.8699830407 + }, + { + "name": "unchanged object", + "iterations": 409600, + "newOutputObjects": 0, + "medianNs": 102.91649658203117, + "p10Ns": 100.44872070312516, + "p90Ns": 110.95527832031271, + "opsPerSecond": 9716615.24839154 + }, + { + "name": "nullable query", + "iterations": 102400, + "newOutputObjects": 3, + "medianNs": 366.9991210937473, + "p10Ns": 361.586357421877, + "p90Ns": 380.5079687500035, + "opsPerSecond": 2724802.1657919916 + }, + { + "name": "connection / 100 rows", + "iterations": 800, + "newOutputObjects": 403, + "medianNs": 63887.45624999956, + "p10Ns": 62050.64749999963, + "p90Ns": 68331.1062499996, + "opsPerSecond": 15652.524903900941 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 1600, + "newOutputObjects": 301, + "medianNs": 27059.063124999906, + "p10Ns": 25591.54562499998, + "p90Ns": 30144.928125000093, + "opsPerSecond": 36956.19450608763 + }, + { + "name": "union list / 100 rows", + "iterations": 800, + "newOutputObjects": 302, + "medianNs": 61811.29499999997, + "p10Ns": 59670.634999999944, + "p90Ns": 67804.75000000025, + "opsPerSecond": 16178.272919213236 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "medianNs": 11185.55546875001, + "p10Ns": 10668.467031249946, + "p90Ns": 12038.666562499997, + "opsPerSecond": 89401.01390527996 + }, + { + "name": "opaque JSON list", + "iterations": 204800, + "newOutputObjects": 0, + "medianNs": 341.34198242187443, + "p10Ns": 329.99998046875146, + "p90Ns": 384.9036718750032, + "opsPerSecond": 2929613.266158603 + }, + { + "name": "recursive inputs", + "iterations": 1600, + "newOutputObjects": 103, + "medianNs": 26979.713124999875, + "p10Ns": 26147.026250000064, + "p90Ns": 32104.163125000014, + "opsPerSecond": 37064.88632280462 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/forward-legacy.json b/packages/rescript-relay/benchmarks/results/forward-legacy.json new file mode 100644 index 00000000..1b11f076 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/forward-legacy.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 2, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "medianNs": 253.89532714843733, + "p10Ns": 238.63674804687486, + "p90Ns": 282.41892089843736, + "opsPerSecond": 3938630.975336384 + }, + { + "name": "unchanged object", + "iterations": 409600, + "newOutputObjects": 0, + "medianNs": 106.58042724609406, + "p10Ns": 103.68308105468738, + "p90Ns": 117.37104003906262, + "opsPerSecond": 9382585.769627301 + }, + { + "name": "nullable query", + "iterations": 102400, + "newOutputObjects": 3, + "medianNs": 412.1633984374995, + "p10Ns": 369.64749023437275, + "p90Ns": 488.93430664062487, + "opsPerSecond": 2426222.2307729735 + }, + { + "name": "connection / 100 rows", + "iterations": 800, + "newOutputObjects": 403, + "medianNs": 63942.409999999654, + "p10Ns": 61311.19749999982, + "p90Ns": 70921.94125000048, + "opsPerSecond": 15639.072721844632 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 1600, + "newOutputObjects": 301, + "medianNs": 27282.629375000055, + "p10Ns": 26822.722500000113, + "p90Ns": 28512.498125000147, + "opsPerSecond": 36653.358672105554 + }, + { + "name": "union list / 100 rows", + "iterations": 800, + "newOutputObjects": 302, + "medianNs": 61514.20750000057, + "p10Ns": 58334.78499999955, + "p90Ns": 72838.58874999965, + "opsPerSecond": 16256.407107252267 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "medianNs": 11270.886875000058, + "p10Ns": 10796.85328124995, + "p90Ns": 12141.31828125005, + "opsPerSecond": 88724.16262273902 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 0, + "medianNs": 324.2976953124988, + "p10Ns": 304.1779296875013, + "p90Ns": 364.02474609374735, + "opsPerSecond": 3083586.5146571053 + }, + { + "name": "recursive inputs", + "iterations": 1600, + "newOutputObjects": 103, + "medianNs": 27698.960000000174, + "p10Ns": 27193.85499999987, + "p90Ns": 33138.649375, + "opsPerSecond": 36102.43850310603 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/forward-master.json b/packages/rescript-relay/benchmarks/results/forward-master.json new file mode 100644 index 00000000..dd94a9f3 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/forward-master.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 2, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "/tmp/rescript-relay-utils-baseline.js", + "results": [ + { + "name": "small fragment", + "iterations": 51200, + "newOutputObjects": 3, + "medianNs": 1296.1905664062479, + "p10Ns": 1248.0368164062506, + "p90Ns": 1352.1872265625, + "opsPerSecond": 771491.4966342867 + }, + { + "name": "unchanged object", + "iterations": 102400, + "newOutputObjects": 1, + "medianNs": 752.9779199218777, + "p10Ns": 736.1401269531243, + "p90Ns": 813.7082519531247, + "opsPerSecond": 1328060.1908004833 + }, + { + "name": "nullable query", + "iterations": 25600, + "newOutputObjects": 3, + "medianNs": 2036.2027343749921, + "p10Ns": 1922.2357421874924, + "p90Ns": 2256.0135937500013, + "opsPerSecond": 491110.23333683313 + }, + { + "name": "connection / 100 rows", + "iterations": 100, + "newOutputObjects": 703, + "medianNs": 742242.8299999955, + "p10Ns": 717182.3600000016, + "p90Ns": 790019.7500000014, + "opsPerSecond": 1347.267982366372 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 400, + "newOutputObjects": 301, + "medianNs": 141903.82750000025, + "p10Ns": 133630.61249999874, + "p90Ns": 161153.9425000001, + "opsPerSecond": 7047.026268547959 + }, + { + "name": "union list / 100 rows", + "iterations": 200, + "newOutputObjects": 302, + "medianNs": 233061.77500000104, + "p10Ns": 222003.52000000293, + "p90Ns": 262139.944999999, + "opsPerSecond": 4290.70790351612 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "medianNs": 11033.36984374991, + "p10Ns": 10773.049531249939, + "p90Ns": 11701.395156249959, + "opsPerSecond": 90634.14117006796 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 2, + "medianNs": 552.1257617187558, + "p10Ns": 541.6073339843753, + "p90Ns": 576.928115234372, + "opsPerSecond": 1811181.5628508641 + }, + { + "name": "recursive inputs", + "iterations": 100, + "newOutputObjects": 203, + "medianNs": 347187.9099999933, + "p10Ns": 328057.15000000416, + "p90Ns": 381132.290000005, + "opsPerSecond": 2880.28462742271 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/forward-pr-673.json b/packages/rescript-relay/benchmarks/results/forward-pr-673.json new file mode 100644 index 00000000..745bfa21 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/forward-pr-673.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 2, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "/tmp/relay-conversion-audit/pr-673-utils.cjs", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "medianNs": 347.25091796874995, + "p10Ns": 340.3625537109378, + "p90Ns": 376.1054150390625, + "opsPerSecond": 2879762.0056687444 + }, + { + "name": "unchanged object", + "iterations": 204800, + "newOutputObjects": 1, + "medianNs": 205.458461914062, + "p10Ns": 197.44585449218644, + "p90Ns": 230.86366210937516, + "opsPerSecond": 4867163.857277751 + }, + { + "name": "nullable query", + "iterations": 102400, + "newOutputObjects": 3, + "medianNs": 612.2728515624987, + "p10Ns": 599.0123437500027, + "p90Ns": 665.727714843749, + "opsPerSecond": 1633258.7627363116 + }, + { + "name": "connection / 100 rows", + "iterations": 200, + "newOutputObjects": 703, + "medianNs": 274419.25500000024, + "p10Ns": 266327.060000001, + "p90Ns": 307860.02000000054, + "opsPerSecond": 3644.0591604987744 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 1600, + "newOutputObjects": 301, + "medianNs": 36741.786875000456, + "p10Ns": 36179.95562500027, + "p90Ns": 40977.7943749998, + "opsPerSecond": 27216.966975561325 + }, + { + "name": "union list / 100 rows", + "iterations": 800, + "newOutputObjects": 302, + "medianNs": 81944.94374999977, + "p10Ns": 77201.3250000009, + "p90Ns": 95192.56124999971, + "opsPerSecond": 12203.315473018465 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "medianNs": 11182.258906249983, + "p10Ns": 10639.661406249987, + "p90Ns": 12689.37812499999, + "opsPerSecond": 89427.36958460875 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 2, + "medianNs": 657.8921874999999, + "p10Ns": 640.9829687499968, + "p90Ns": 686.3424511718641, + "opsPerSecond": 1520005.890022824 + }, + { + "name": "recursive inputs", + "iterations": 400, + "newOutputObjects": 203, + "medianNs": 116333.15999999922, + "p10Ns": 111598.26000000066, + "p90Ns": 139304.8100000033, + "opsPerSecond": 8596.00134647771 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/forward-prepared.json b/packages/rescript-relay/benchmarks/results/forward-prepared.json new file mode 100644 index 00000000..36a1470a --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/forward-prepared.json @@ -0,0 +1,102 @@ +{ + "fixtureVersion": 2, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 4519.663999999999, + "medianNs": 111.94897216796868, + "p10Ns": 106.89505615234351, + "p90Ns": 128.51202148437497, + "opsPerSecond": 8932641.190305848 + }, + { + "name": "unchanged object", + "iterations": 819200, + "newOutputObjects": 0, + "preparationNs": 1350.582999999915, + "medianNs": 58.80991210937519, + "p10Ns": 56.25684326171859, + "p90Ns": 65.2856909179686, + "opsPerSecond": 17003936.311623648 + }, + { + "name": "nullable query", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 1025.3990000001068, + "medianNs": 148.41553710937484, + "p10Ns": 145.0156787109369, + "p90Ns": 168.32671142578135, + "opsPerSecond": 6737839.039473677 + }, + { + "name": "connection / 100 rows", + "iterations": 1600, + "newOutputObjects": 403, + "preparationNs": 3402.5219999998626, + "medianNs": 24011.720624999954, + "p10Ns": 23565.928124999973, + "p90Ns": 26119.755624999925, + "opsPerSecond": 41646.328291811114 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 3200, + "newOutputObjects": 301, + "preparationNs": 1177.1159999998417, + "medianNs": 16457.669374999994, + "p10Ns": 16010.871874999993, + "p90Ns": 18624.978750000024, + "opsPerSecond": 60761.94491542338 + }, + { + "name": "union list / 100 rows", + "iterations": 3200, + "newOutputObjects": 302, + "preparationNs": 3215.197000000444, + "medianNs": 18512.08093749989, + "p10Ns": 17770.053437500053, + "p90Ns": 21976.89937500002, + "opsPerSecond": 54018.77851421348 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "preparationNs": 1789.2320000000836, + "medianNs": 12559.793281249937, + "p10Ns": 11876.653593750036, + "p90Ns": 13619.199218749998, + "opsPerSecond": 79619.14480653626 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 0, + "preparationNs": 1626.9050000000789, + "medianNs": 446.64761718749446, + "p10Ns": 422.79999999999876, + "p90Ns": 457.6975195312461, + "opsPerSecond": 2238901.4550148565 + }, + { + "name": "recursive inputs", + "iterations": 3200, + "newOutputObjects": 103, + "preparationNs": 3418.211999999585, + "medianNs": 18089.162812499922, + "p10Ns": 17540.556874999995, + "p90Ns": 19130.46531249989, + "opsPerSecond": 55281.71814059757 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/artifact-size.json b/packages/rescript-relay/benchmarks/results/js-specialization/artifact-size.json new file mode 100644 index 00000000..44689418 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/artifact-size.json @@ -0,0 +1,792 @@ +{ + "fixtureVersion": 1, + "esbuild": "0.25.12", + "node": "v24.16.0", + "artifactCount": 129, + "combined": { + "minified": 272639, + "gzip": 36319, + "brotli": 26233 + }, + "separate": { + "minified": 302536, + "gzip": 118381, + "brotli": 104973 + }, + "files": [ + { + "path": "__tests__/__generated__/GroupAvatar_group_graphql.bs.js", + "minified": 711, + "gzip": 447, + "brotli": 384 + }, + { + "path": "__tests__/__generated__/HasNameComponent_hasName_graphql.bs.js", + "minified": 805, + "gzip": 467, + "brotli": 400 + }, + { + "path": "__tests__/__generated__/LocalUser____relay_model_instance_graphql.bs.js", + "minified": 1059, + "gzip": 579, + "brotli": 506 + }, + { + "path": "__tests__/__generated__/LocalUser__id_graphql.bs.js", + "minified": 747, + "gzip": 462, + "brotli": 399 + }, + { + "path": "__tests__/__generated__/LocalUser_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/Query_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/RichContent_content_graphql.bs.js", + "minified": 722, + "gzip": 447, + "brotli": 380 + }, + { + "path": "__tests__/__generated__/TestAliasedFragmentsQuery_graphql.bs.js", + "minified": 3136, + "gzip": 1125, + "brotli": 1003 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.bs.js", + "minified": 732, + "gzip": 455, + "brotli": 394 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userLastName_graphql.bs.js", + "minified": 730, + "gzip": 454, + "brotli": 391 + }, + { + "path": "__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.bs.js", + "minified": 2792, + "gzip": 1191, + "brotli": 1066 + }, + { + "path": "__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.bs.js", + "minified": 2721, + "gzip": 1165, + "brotli": 1035 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.bs.js", + "minified": 3889, + "gzip": 1386, + "brotli": 1260 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropQuery_graphql.bs.js", + "minified": 3020, + "gzip": 1285, + "brotli": 1159 + }, + { + "path": "__tests__/__generated__/TestCatchMembersPropQuery_graphql.bs.js", + "minified": 3708, + "gzip": 1381, + "brotli": 1244 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMemberQuery_graphql.bs.js", + "minified": 3073, + "gzip": 1217, + "brotli": 1078 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMember_member_graphql.bs.js", + "minified": 1472, + "gzip": 691, + "brotli": 604 + }, + { + "path": "__tests__/__generated__/TestCatchUser_user_graphql.bs.js", + "minified": 875, + "gzip": 542, + "brotli": 465 + }, + { + "path": "__tests__/__generated__/TestCodesplitQuery_graphql.bs.js", + "minified": 7005, + "gzip": 1782, + "brotli": 1615 + }, + { + "path": "__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.bs.js", + "minified": 731, + "gzip": 458, + "brotli": 393 + }, + { + "path": "__tests__/__generated__/TestConnections2_user_graphql.bs.js", + "minified": 4007, + "gzip": 1328, + "brotli": 1202 + }, + { + "path": "__tests__/__generated__/TestConnections3_user_graphql.bs.js", + "minified": 2215, + "gzip": 817, + "brotli": 726 + }, + { + "path": "__tests__/__generated__/TestConnectionsQuery_graphql.bs.js", + "minified": 4183, + "gzip": 1508, + "brotli": 1348 + }, + { + "path": "__tests__/__generated__/TestConnections_AddFriendMutation_graphql.bs.js", + "minified": 2162, + "gzip": 856, + "brotli": 771 + }, + { + "path": "__tests__/__generated__/TestConnections_user_graphql.bs.js", + "minified": 2895, + "gzip": 1031, + "brotli": 928 + }, + { + "path": "__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2616, + "gzip": 1119, + "brotli": 992 + }, + { + "path": "__tests__/__generated__/TestCustomScalarsQuery_graphql.bs.js", + "minified": 4182, + "gzip": 1475, + "brotli": 1336 + }, + { + "path": "__tests__/__generated__/TestFragmentQuery_graphql.bs.js", + "minified": 4301, + "gzip": 1367, + "brotli": 1223 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.bs.js", + "minified": 903, + "gzip": 534, + "brotli": 463 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredQuery_graphql.bs.js", + "minified": 3467, + "gzip": 1182, + "brotli": 1050 + }, + { + "path": "__tests__/__generated__/TestFragmentRequired_user_graphql.bs.js", + "minified": 890, + "gzip": 525, + "brotli": 452 + }, + { + "path": "__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.bs.js", + "minified": 925, + "gzip": 524, + "brotli": 451 + }, + { + "path": "__tests__/__generated__/TestFragment_inline_graphql.bs.js", + "minified": 694, + "gzip": 430, + "brotli": 372 + }, + { + "path": "__tests__/__generated__/TestFragment_plural_user_graphql.bs.js", + "minified": 996, + "gzip": 531, + "brotli": 458 + }, + { + "path": "__tests__/__generated__/TestFragment_sub_user_graphql.bs.js", + "minified": 718, + "gzip": 450, + "brotli": 387 + }, + { + "path": "__tests__/__generated__/TestFragment_user_graphql.bs.js", + "minified": 1168, + "gzip": 600, + "brotli": 513 + }, + { + "path": "__tests__/__generated__/TestInputUnionQuery_graphql.bs.js", + "minified": 2163, + "gzip": 1037, + "brotli": 904 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadQuery_graphql.bs.js", + "minified": 5054, + "gzip": 1560, + "brotli": 1408 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.bs.js", + "minified": 5686, + "gzip": 1693, + "brotli": 1525 + }, + { + "path": "__tests__/__generated__/TestLocalPayload_user_graphql.bs.js", + "minified": 2408, + "gzip": 847, + "brotli": 753 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.bs.js", + "minified": 2185, + "gzip": 961, + "brotli": 844 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.bs.js", + "minified": 2708, + "gzip": 1169, + "brotli": 1043 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.bs.js", + "minified": 2208, + "gzip": 963, + "brotli": 846 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.bs.js", + "minified": 2192, + "gzip": 963, + "brotli": 843 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.bs.js", + "minified": 2183, + "gzip": 956, + "brotli": 844 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.bs.js", + "minified": 2704, + "gzip": 1167, + "brotli": 1040 + }, + { + "path": "__tests__/__generated__/TestMutationInline_user_graphql.bs.js", + "minified": 698, + "gzip": 434, + "brotli": 376 + }, + { + "path": "__tests__/__generated__/TestMutationProvidedVariable_user_graphql.bs.js", + "minified": 898, + "gzip": 514, + "brotli": 443 + }, + { + "path": "__tests__/__generated__/TestMutationQuery_graphql.bs.js", + "minified": 3201, + "gzip": 1208, + "brotli": 1066 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.bs.js", + "minified": 2404, + "gzip": 990, + "brotli": 882 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.bs.js", + "minified": 2994, + "gzip": 1025, + "brotli": 923 + }, + { + "path": "__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 2929, + "gzip": 1004, + "brotli": 900 + }, + { + "path": "__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 3910, + "gzip": 1231, + "brotli": 1116 + }, + { + "path": "__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.bs.js", + "minified": 2788, + "gzip": 967, + "brotli": 880 + }, + { + "path": "__tests__/__generated__/TestMutation_user_graphql.bs.js", + "minified": 1757, + "gzip": 732, + "brotli": 645 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.bs.js", + "minified": 3137, + "gzip": 1238, + "brotli": 1103 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceQuery_graphql.bs.js", + "minified": 3720, + "gzip": 1318, + "brotli": 1193 + }, + { + "path": "__tests__/__generated__/TestNodeInterface_user_graphql.bs.js", + "minified": 720, + "gzip": 454, + "brotli": 387 + }, + { + "path": "__tests__/__generated__/TestNonReactQuery_graphql.bs.js", + "minified": 2468, + "gzip": 1077, + "brotli": 944 + }, + { + "path": "__tests__/__generated__/TestNonReact_user_graphql.bs.js", + "minified": 914, + "gzip": 519, + "brotli": 447 + }, + { + "path": "__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2798, + "gzip": 1170, + "brotli": 1035 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesMutation_graphql.bs.js", + "minified": 2713, + "gzip": 1016, + "brotli": 908 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesQuery_graphql.bs.js", + "minified": 2141, + "gzip": 948, + "brotli": 834 + }, + { + "path": "__tests__/__generated__/TestObserverQuery_graphql.bs.js", + "minified": 1893, + "gzip": 922, + "brotli": 813 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeQuery_graphql.bs.js", + "minified": 4371, + "gzip": 1458, + "brotli": 1305 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.bs.js", + "minified": 4990, + "gzip": 1625, + "brotli": 1474 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_query_graphql.bs.js", + "minified": 2756, + "gzip": 1055, + "brotli": 940 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_user_graphql.bs.js", + "minified": 1062, + "gzip": 549, + "brotli": 475 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionQuery_graphql.bs.js", + "minified": 4940, + "gzip": 1564, + "brotli": 1406 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.bs.js", + "minified": 5625, + "gzip": 1722, + "brotli": 1554 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_query_graphql.bs.js", + "minified": 3741, + "gzip": 1239, + "brotli": 1113 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_user_graphql.bs.js", + "minified": 993, + "gzip": 541, + "brotli": 471 + }, + { + "path": "__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.bs.js", + "minified": 2722, + "gzip": 1112, + "brotli": 991 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.bs.js", + "minified": 3657, + "gzip": 1295, + "brotli": 1149 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.bs.js", + "minified": 4215, + "gzip": 1439, + "brotli": 1293 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.bs.js", + "minified": 1106, + "gzip": 555, + "brotli": 486 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user_graphql.bs.js", + "minified": 2208, + "gzip": 879, + "brotli": 776 + }, + { + "path": "__tests__/__generated__/TestProvidedVariablesQuery_graphql.bs.js", + "minified": 5647, + "gzip": 1538, + "brotli": 1388 + }, + { + "path": "__tests__/__generated__/TestProvidedVariables_user_graphql.bs.js", + "minified": 1581, + "gzip": 572, + "brotli": 504 + }, + { + "path": "__tests__/__generated__/TestQueryInputObjectsQuery_graphql.bs.js", + "minified": 2061, + "gzip": 1018, + "brotli": 892 + }, + { + "path": "__tests__/__generated__/TestQuery_graphql.bs.js", + "minified": 2613, + "gzip": 1106, + "brotli": 978 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeQuery_graphql.bs.js", + "minified": 3713, + "gzip": 1409, + "brotli": 1274 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.bs.js", + "minified": 3822, + "gzip": 1412, + "brotli": 1264 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNode_user_graphql.bs.js", + "minified": 1705, + "gzip": 769, + "brotli": 668 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.bs.js", + "minified": 2193, + "gzip": 1016, + "brotli": 902 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgs_query_graphql.bs.js", + "minified": 988, + "gzip": 558, + "brotli": 485 + }, + { + "path": "__tests__/__generated__/TestRefetchingQuery_graphql.bs.js", + "minified": 3719, + "gzip": 1387, + "brotli": 1234 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.bs.js", + "minified": 2235, + "gzip": 1022, + "brotli": 905 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.bs.js", + "minified": 1000, + "gzip": 561, + "brotli": 489 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordQuery_graphql.bs.js", + "minified": 3761, + "gzip": 1393, + "brotli": 1240 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.bs.js", + "minified": 4514, + "gzip": 1581, + "brotli": 1426 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecord_user_graphql.bs.js", + "minified": 2080, + "gzip": 827, + "brotli": 721 + }, + { + "path": "__tests__/__generated__/TestRefetchingRefetchQuery_graphql.bs.js", + "minified": 4472, + "gzip": 1574, + "brotli": 1420 + }, + { + "path": "__tests__/__generated__/TestRefetching_user_graphql.bs.js", + "minified": 2068, + "gzip": 823, + "brotli": 721 + }, + { + "path": "__tests__/__generated__/TestRelayResolversAllQuery_graphql.bs.js", + "minified": 5361, + "gzip": 1536, + "brotli": 1386 + }, + { + "path": "__tests__/__generated__/TestRelayResolversQuery_graphql.bs.js", + "minified": 3046, + "gzip": 1181, + "brotli": 1052 + }, + { + "path": "__tests__/__generated__/TestRelayResolvers_user_graphql.bs.js", + "minified": 1300, + "gzip": 625, + "brotli": 542 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver2_graphql.bs.js", + "minified": 794, + "gzip": 465, + "brotli": 395 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver_graphql.bs.js", + "minified": 793, + "gzip": 463, + "brotli": 395 + }, + { + "path": "__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.bs.js", + "minified": 2191, + "gzip": 967, + "brotli": 855 + }, + { + "path": "__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.bs.js", + "minified": 1796, + "gzip": 841, + "brotli": 735 + }, + { + "path": "__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.bs.js", + "minified": 1829, + "gzip": 827, + "brotli": 719 + }, + { + "path": "__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.bs.js", + "minified": 902, + "gzip": 515, + "brotli": 443 + }, + { + "path": "__tests__/__generated__/TestSubscriptionQuery_graphql.bs.js", + "minified": 2542, + "gzip": 1073, + "brotli": 942 + }, + { + "path": "__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.bs.js", + "minified": 2552, + "gzip": 952, + "brotli": 837 + }, + { + "path": "__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.bs.js", + "minified": 3038, + "gzip": 1066, + "brotli": 953 + }, + { + "path": "__tests__/__generated__/TestSubscription_user_graphql.bs.js", + "minified": 1061, + "gzip": 535, + "brotli": 455 + }, + { + "path": "__tests__/__generated__/TestTestingHelpersQuery_graphql.bs.js", + "minified": 3448, + "gzip": 1298, + "brotli": 1170 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_plural_user_graphql.bs.js", + "minified": 1002, + "gzip": 538, + "brotli": 466 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_sub_user_graphql.bs.js", + "minified": 724, + "gzip": 458, + "brotli": 390 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_user_graphql.bs.js", + "minified": 1180, + "gzip": 607, + "brotli": 521 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentQuery_graphql.bs.js", + "minified": 3556, + "gzip": 1264, + "brotli": 1124 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentUser_user_graphql.bs.js", + "minified": 923, + "gzip": 520, + "brotli": 448 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_member_graphql.bs.js", + "minified": 1547, + "gzip": 696, + "brotli": 609 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_plural_member_graphql.bs.js", + "minified": 1561, + "gzip": 707, + "brotli": 634 + }, + { + "path": "__tests__/__generated__/TestUnionsQuery_graphql.bs.js", + "minified": 5037, + "gzip": 1522, + "brotli": 1380 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.bs.js", + "minified": 2816, + "gzip": 1121, + "brotli": 997 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_query_graphql.bs.js", + "minified": 1248, + "gzip": 607, + "brotli": 517 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_updatableUser_graphql.bs.js", + "minified": 678, + "gzip": 363, + "brotli": 321 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.bs.js", + "minified": 1298, + "gzip": 610, + "brotli": 529 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_graphql.bs.js", + "minified": 2607, + "gzip": 1046, + "brotli": 936 + }, + { + "path": "__tests__/__generated__/UserAvatar_user_graphql.bs.js", + "minified": 1049, + "gzip": 572, + "brotli": 494 + }, + { + "path": "__tests__/__generated__/UserMeta____relay_model_instance_graphql.bs.js", + "minified": 785, + "gzip": 476, + "brotli": 404 + }, + { + "path": "__tests__/__generated__/UserMeta_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/UserName_user_graphql.bs.js", + "minified": 785, + "gzip": 458, + "brotli": 393 + }, + { + "path": "__tests__/__generated__/User_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.bs.js", + "minified": 1418, + "gzip": 570, + "brotli": 499 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.bs.js", + "minified": 5051, + "gzip": 1484, + "brotli": 1332 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.bs.js", + "minified": 2236, + "gzip": 845, + "brotli": 759 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-final-forward.json b/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-final-forward.json new file mode 100644 index 00000000..df90cf86 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-final-forward.json @@ -0,0 +1,102 @@ +{ + "fixtureVersion": 3, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 9, + "durationMs": 25, + "implementation": "src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 6166.008999999999, + "medianNs": 97.32978027343741, + "p10Ns": 93.57615478515616, + "p90Ns": 106.187236328125, + "opsPerSecond": 10274347.657937879 + }, + { + "name": "unchanged object", + "iterations": 819200, + "newOutputObjects": 0, + "preparationNs": 0, + "medianNs": 43.0706787109375, + "p10Ns": 42.05908691406246, + "p90Ns": 68.60466064453118, + "opsPerSecond": 23217651.310102455 + }, + { + "name": "nullable query", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 0, + "medianNs": 120.18717285156245, + "p10Ns": 114.3510473632814, + "p90Ns": 192.49034912109386, + "opsPerSecond": 8320355.461185971 + }, + { + "name": "connection / 100 rows", + "iterations": 1600, + "newOutputObjects": 403, + "preparationNs": 3310.2939999998853, + "medianNs": 16611.42062500005, + "p10Ns": 16069.923124999974, + "p90Ns": 29744.175624999938, + "opsPerSecond": 60199.5472015806 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 3200, + "newOutputObjects": 301, + "preparationNs": 1250.7120000000214, + "medianNs": 12767.827812500058, + "p10Ns": 12063.315937499938, + "p90Ns": 19323.372500000034, + "opsPerSecond": 78321.8582428698 + }, + { + "name": "union list / 100 rows", + "iterations": 3200, + "newOutputObjects": 302, + "preparationNs": 2579.252999999881, + "medianNs": 15920.618749999989, + "p10Ns": 15137.376874999973, + "p90Ns": 24660.72499999996, + "opsPerSecond": 62811.62910204107 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "preparationNs": 1769.7540000003755, + "medianNs": 12097.4425, + "p10Ns": 11560.428437500006, + "p90Ns": 16572.76968749997, + "opsPerSecond": 82662.09986118968 + }, + { + "name": "opaque JSON list", + "iterations": 204800, + "newOutputObjects": 0, + "preparationNs": 1671.7290000001412, + "medianNs": 157.8011572265625, + "p10Ns": 151.1920361328123, + "p90Ns": 209.99336425781223, + "opsPerSecond": 6337089.141648392 + }, + { + "name": "recursive inputs", + "iterations": 1600, + "newOutputObjects": 103, + "preparationNs": 3554.1239999997742, + "medianNs": 15456.345000000056, + "p10Ns": 14703.716250000129, + "p90Ns": 18651.30625000006, + "opsPerSecond": 64698.34880109084 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-final-reverse.json b/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-final-reverse.json new file mode 100644 index 00000000..0b745557 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-final-reverse.json @@ -0,0 +1,102 @@ +{ + "fixtureVersion": 3, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 9, + "durationMs": 25, + "implementation": "src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "preparationNs": 3729.476000000002, + "medianNs": 96.39559570312498, + "p10Ns": 95.44668457031239, + "p90Ns": 195.75591796875, + "opsPerSecond": 10373917.944132604 + }, + { + "name": "unchanged object", + "iterations": 819200, + "newOutputObjects": 0, + "preparationNs": 0, + "medianNs": 42.319431152343796, + "p10Ns": 41.3546801757813, + "p90Ns": 74.16593627929694, + "opsPerSecond": 23629807.224963527 + }, + { + "name": "nullable query", + "iterations": 204800, + "newOutputObjects": 3, + "preparationNs": 0, + "medianNs": 116.07211914062498, + "p10Ns": 111.87082519531266, + "p90Ns": 131.54970703125045, + "opsPerSecond": 8615333.358293122 + }, + { + "name": "connection / 100 rows", + "iterations": 1600, + "newOutputObjects": 403, + "preparationNs": 4470.445999999924, + "medianNs": 17709.131875, + "p10Ns": 16565.07750000003, + "p90Ns": 30430.39124999993, + "opsPerSecond": 56468.04185877124 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 3200, + "newOutputObjects": 301, + "preparationNs": 1157.4470000000474, + "medianNs": 12995.436249999984, + "p10Ns": 12703.703749999988, + "p90Ns": 21948.250625000015, + "opsPerSecond": 76950.09084439171 + }, + { + "name": "union list / 100 rows", + "iterations": 1600, + "newOutputObjects": 302, + "preparationNs": 3264.897999999903, + "medianNs": 17192.819375, + "p10Ns": 16707.176875000016, + "p90Ns": 27857.229374999977, + "opsPerSecond": 58163.8170092158 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "preparationNs": 1576.9289999998364, + "medianNs": 11855.260312499922, + "p10Ns": 11338.465312500004, + "p90Ns": 12947.777499999944, + "opsPerSecond": 84350.74166575847 + }, + { + "name": "opaque JSON list", + "iterations": 204800, + "newOutputObjects": 0, + "preparationNs": 1933.8830000001508, + "medianNs": 152.8652148437515, + "p10Ns": 139.65491210937486, + "p90Ns": 172.61145996093853, + "opsPerSecond": 6541710.624108517 + }, + { + "name": "recursive inputs", + "iterations": 3200, + "newOutputObjects": 103, + "preparationNs": 3667.729000000236, + "medianNs": 16676.562500000073, + "p10Ns": 15707.992187499967, + "p90Ns": 24262.58218749993, + "opsPerSecond": 59964.39613979174 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-main-forward.json b/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-main-forward.json new file mode 100644 index 00000000..bdcd6606 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-main-forward.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 3, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 9, + "durationMs": 25, + "implementation": "/tmp/relay-final-main-comparison/main.cjs", + "results": [ + { + "name": "small fragment", + "iterations": 25600, + "newOutputObjects": 3, + "medianNs": 1304.318124999999, + "p10Ns": 1260.6379296875002, + "p90Ns": 1445.466796875, + "opsPerSecond": 766684.1247030902 + }, + { + "name": "unchanged object", + "iterations": 51200, + "newOutputObjects": 1, + "medianNs": 772.4417578125009, + "p10Ns": 754.6286132812497, + "p90Ns": 1082.844199218751, + "opsPerSecond": 1294595.9871873416 + }, + { + "name": "nullable query", + "iterations": 12800, + "newOutputObjects": 3, + "medianNs": 2048.8332812500066, + "p10Ns": 1998.1547656249977, + "p90Ns": 3130.81101562501, + "opsPerSecond": 488082.66106937383 + }, + { + "name": "connection / 100 rows", + "iterations": 100, + "newOutputObjects": 703, + "medianNs": 751973.3699999984, + "p10Ns": 717691.5099999997, + "p90Ns": 896565.6400000012, + "opsPerSecond": 1329.834326446962 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 200, + "newOutputObjects": 301, + "medianNs": 149238.90000000029, + "p10Ns": 135191.16500000108, + "p90Ns": 230516.00500000175, + "opsPerSecond": 6700.665845165021 + }, + { + "name": "union list / 100 rows", + "iterations": 100, + "newOutputObjects": 302, + "medianNs": 229196.509999997, + "p10Ns": 225978.93000000113, + "p90Ns": 238393.49000000313, + "opsPerSecond": 4363.068181099324 + }, + { + "name": "custom scalar list", + "iterations": 1600, + "newOutputObjects": 102, + "medianNs": 11483.595624999907, + "p10Ns": 11311.169999999891, + "p90Ns": 19132.070624999924, + "opsPerSecond": 87080.73957454489 + }, + { + "name": "opaque JSON list", + "iterations": 51200, + "newOutputObjects": 2, + "medianNs": 581.3015429687507, + "p10Ns": 571.5029687500017, + "p90Ns": 931.3527343750039, + "opsPerSecond": 1720277.5600644802 + }, + { + "name": "recursive inputs", + "iterations": 100, + "newOutputObjects": 203, + "medianNs": 355228.6199999981, + "p10Ns": 330462.7299999993, + "p90Ns": 382119.9200000001, + "opsPerSecond": 2815.088491462218 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-main-reverse.json b/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-main-reverse.json new file mode 100644 index 00000000..923f66b2 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/main-comparison-main-reverse.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 3, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 9, + "durationMs": 25, + "implementation": "/tmp/relay-final-main-comparison/main.cjs", + "results": [ + { + "name": "small fragment", + "iterations": 12800, + "newOutputObjects": 3, + "medianNs": 1378.5340625000008, + "p10Ns": 1325.6826562500003, + "p90Ns": 1747.93515625, + "opsPerSecond": 725408.2631708634 + }, + { + "name": "unchanged object", + "iterations": 51200, + "newOutputObjects": 1, + "medianNs": 799.3844726562504, + "p10Ns": 753.2267773437505, + "p90Ns": 1190.4633593750002, + "opsPerSecond": 1250962.502032508 + }, + { + "name": "nullable query", + "iterations": 12800, + "newOutputObjects": 3, + "medianNs": 1988.8723437500032, + "p10Ns": 1969.679062499994, + "p90Ns": 2078.2349218749996, + "opsPerSecond": 502797.4787534668 + }, + { + "name": "connection / 100 rows", + "iterations": 100, + "newOutputObjects": 703, + "medianNs": 729746.7899999992, + "p10Ns": 719330.2099999982, + "p90Ns": 893077.760000001, + "opsPerSecond": 1370.3383333827355 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 200, + "newOutputObjects": 301, + "medianNs": 149771.75999999986, + "p10Ns": 130623.02999999929, + "p90Ns": 211133.4850000003, + "opsPerSecond": 6676.826125298928 + }, + { + "name": "union list / 100 rows", + "iterations": 200, + "newOutputObjects": 302, + "medianNs": 221552.9249999986, + "p10Ns": 218264.11500000174, + "p90Ns": 326365.54499999876, + "opsPerSecond": 4513.594212308442 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "medianNs": 11099.958437499992, + "p10Ns": 10862.159062499897, + "p90Ns": 11798.778437500061, + "opsPerSecond": 90090.42742192706 + }, + { + "name": "opaque JSON list", + "iterations": 51200, + "newOutputObjects": 2, + "medianNs": 545.0711914062545, + "p10Ns": 537.633710937504, + "p90Ns": 711.2100781249975, + "opsPerSecond": 1834622.7350964076 + }, + { + "name": "recursive inputs", + "iterations": 100, + "newOutputObjects": 203, + "medianNs": 320075.90999999596, + "p10Ns": 299301.76000000304, + "p90Ns": 493538.5300000007, + "opsPerSecond": 3124.2588672168818 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/paired-final.json b/packages/rescript-relay/benchmarks/results/js-specialization/paired-final.json new file mode 100644 index 00000000..3580295f --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/paired-final.json @@ -0,0 +1,1237 @@ +{ + "fixtureVersion": 3, + "node": "v24.16.0", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "cpuAffinity": "7", + "samples": 31, + "targetSampleMs": 20, + "initialLoad": [ + 18.66, + 18.35, + 19.61 + ], + "finalLoad": [ + 18.51, + 18.31, + 19.56 + ], + "implementations": { + "baseline": "/tmp/relay-js-sweep/baseline/utils.js", + "candidate": "/tmp/rescript-relay-conversion-perf/packages/rescript-relay/src/utils.js" + }, + "sha256": { + "baseline": "7f68600a1480e8bfa22b754383a9734dcbaaccc55f7fbf923c06489864287294", + "preparedBaseline": "da045e26c642da31d9b9808b840b65852bd58fbf683d1f5b3ba6ad80e60b2847", + "candidate": "7f68600a1480e8bfa22b754383a9734dcbaaccc55f7fbf923c06489864287294", + "preparedCandidate": "db39247dc78284162c7fe25551523d1681c4e469d37fceb7e24912b542a2e5c9", + "fixtures": "d660fba22de94a8c016e773830d1c9f1b24fc3ce46bfd2016c5e6d66de57d164" + }, + "results": [ + { + "mode": "prepared", + "name": "small fragment", + "count": 409600, + "baselineNs": 101.97609375000027, + "candidateNs": 93.63988037109361, + "ratio": 0.911205684427288, + "samples": [ + { + "baselineNs": 100.61977294921871, + "candidateNs": 90.84837646484378 + }, + { + "baselineNs": 100.75635742187491, + "candidateNs": 91.80976562499998 + }, + { + "baselineNs": 103.16593994140631, + "candidateNs": 93.6941845703125 + }, + { + "baselineNs": 99.6786108398437, + "candidateNs": 92.54249755859368 + }, + { + "baselineNs": 134.55474365234366, + "candidateNs": 111.5397485351563 + }, + { + "baselineNs": 98.84574218750025, + "candidateNs": 90.67483154296868 + }, + { + "baselineNs": 99.98926025390625, + "candidateNs": 90.29442626953127 + }, + { + "baselineNs": 99.4406372070311, + "candidateNs": 90.73456298828108 + }, + { + "baselineNs": 98.90339355468736, + "candidateNs": 95.48519042968745 + }, + { + "baselineNs": 100.15113525390639, + "candidateNs": 90.33111572265607 + }, + { + "baselineNs": 121.9141601562501, + "candidateNs": 122.87283203125054 + }, + { + "baselineNs": 98.94866943359393, + "candidateNs": 90.9881665039064 + }, + { + "baselineNs": 101.673447265625, + "candidateNs": 93.51202880859388 + }, + { + "baselineNs": 99.20073242187522, + "candidateNs": 98.96845703124991 + }, + { + "baselineNs": 104.69392578125014, + "candidateNs": 91.67628417968754 + }, + { + "baselineNs": 101.97609375000027, + "candidateNs": 100.5320556640621 + }, + { + "baselineNs": 138.60612548828144, + "candidateNs": 134.11096191406236 + }, + { + "baselineNs": 107.09956787109343, + "candidateNs": 96.55134033203116 + }, + { + "baselineNs": 101.21009521484369, + "candidateNs": 119.24979980468709 + }, + { + "baselineNs": 126.60336669921836, + "candidateNs": 91.62239746093759 + }, + { + "baselineNs": 100.39953125, + "candidateNs": 119.72046386718783 + }, + { + "baselineNs": 183.26341064453078, + "candidateNs": 95.66017822265627 + }, + { + "baselineNs": 126.73002197265637, + "candidateNs": 93.63988037109361 + }, + { + "baselineNs": 129.81686279296855, + "candidateNs": 92.15538818359347 + }, + { + "baselineNs": 101.77635253906249, + "candidateNs": 94.67819824218738 + }, + { + "baselineNs": 127.6537646484377, + "candidateNs": 103.95960449218755 + }, + { + "baselineNs": 103.5758935546871, + "candidateNs": 94.03039306640659 + }, + { + "baselineNs": 106.15442382812535, + "candidateNs": 149.54958251953033 + }, + { + "baselineNs": 103.78309814453202, + "candidateNs": 92.74028076171948 + }, + { + "baselineNs": 102.98172851562536, + "candidateNs": 92.17762207031188 + }, + { + "baselineNs": 99.00397216796918, + "candidateNs": 90.59088378906232 + } + ] + }, + { + "mode": "prepared", + "name": "unchanged object", + "count": 819200, + "baselineNs": 57.85585937500004, + "candidateNs": 49.18847900390655, + "ratio": 0.8518584576886907, + "samples": [ + { + "baselineNs": 57.112375488281295, + "candidateNs": 48.563592529296535 + }, + { + "baselineNs": 56.940970458984054, + "candidateNs": 48.91904907226574 + }, + { + "baselineNs": 58.13724853515611, + "candidateNs": 48.95136108398424 + }, + { + "baselineNs": 58.262864990234476, + "candidateNs": 49.48025390624999 + }, + { + "baselineNs": 57.68380493164082, + "candidateNs": 49.63090454101537 + }, + { + "baselineNs": 63.46085937500024, + "candidateNs": 79.54098876953086 + }, + { + "baselineNs": 62.74087036132803, + "candidateNs": 58.41883666992176 + }, + { + "baselineNs": 68.73973022460954, + "candidateNs": 59.7001879882808 + }, + { + "baselineNs": 66.4359020996097, + "candidateNs": 63.16422851562531 + }, + { + "baselineNs": 67.74928466796837, + "candidateNs": 50.83255371093775 + }, + { + "baselineNs": 78.52408813476598, + "candidateNs": 48.905118408202334 + }, + { + "baselineNs": 56.42544555664153, + "candidateNs": 48.873272705077795 + }, + { + "baselineNs": 57.33201782226472, + "candidateNs": 48.2005651855466 + }, + { + "baselineNs": 57.25826904296904, + "candidateNs": 49.00488037109407 + }, + { + "baselineNs": 57.07322631835931, + "candidateNs": 48.61831054687516 + }, + { + "baselineNs": 76.85287597656188, + "candidateNs": 56.13781738281309 + }, + { + "baselineNs": 56.78581787109338, + "candidateNs": 48.43781738281261 + }, + { + "baselineNs": 57.153603515625036, + "candidateNs": 48.66701171875043 + }, + { + "baselineNs": 58.79461914062523, + "candidateNs": 49.29632446289078 + }, + { + "baselineNs": 58.03193481445312, + "candidateNs": 48.39604003906262 + }, + { + "baselineNs": 57.443836669921474, + "candidateNs": 61.369467773437776 + }, + { + "baselineNs": 56.97390624999966, + "candidateNs": 65.04063842773466 + }, + { + "baselineNs": 57.3483325195312, + "candidateNs": 51.23844604492223 + }, + { + "baselineNs": 58.24804199218825, + "candidateNs": 49.18847900390655 + }, + { + "baselineNs": 57.6477746582027, + "candidateNs": 48.99093749999972 + }, + { + "baselineNs": 56.982393798827864, + "candidateNs": 48.69511596679698 + }, + { + "baselineNs": 78.29868774414051, + "candidateNs": 58.60127319335939 + }, + { + "baselineNs": 57.07952514648507, + "candidateNs": 48.54974853515714 + }, + { + "baselineNs": 57.85585937500004, + "candidateNs": 50.296958007812755 + }, + { + "baselineNs": 61.29579345703129, + "candidateNs": 48.92166748046822 + }, + { + "baselineNs": 58.80549194335894, + "candidateNs": 51.6028552246095 + } + ] + }, + { + "mode": "prepared", + "name": "nullable query", + "count": 102400, + "baselineNs": 150.39659179687524, + "candidateNs": 121.40841796874469, + "ratio": 0.8109976437073325, + "samples": [ + { + "baselineNs": 150.82689453125298, + "candidateNs": 122.490830078128 + }, + { + "baselineNs": 153.11520507812483, + "candidateNs": 126.57667968750452 + }, + { + "baselineNs": 154.33058593750104, + "candidateNs": 124.04954101563172 + }, + { + "baselineNs": 146.09148437500608, + "candidateNs": 122.96360351562006 + }, + { + "baselineNs": 148.65794921875343, + "candidateNs": 118.6480273437507 + }, + { + "baselineNs": 146.67754882812469, + "candidateNs": 118.95514648437633 + }, + { + "baselineNs": 150.39659179687524, + "candidateNs": 123.02005859375242 + }, + { + "baselineNs": 148.088125000001, + "candidateNs": 121.40841796874469 + }, + { + "baselineNs": 149.74859375000094, + "candidateNs": 118.78402343749705 + }, + { + "baselineNs": 152.00167968749236, + "candidateNs": 130.23515625000036 + }, + { + "baselineNs": 149.78705078124932, + "candidateNs": 118.23219726562861 + }, + { + "baselineNs": 148.54338867187343, + "candidateNs": 118.81141601562284 + }, + { + "baselineNs": 148.29016601562552, + "candidateNs": 123.51748046874889 + }, + { + "baselineNs": 151.31072265624823, + "candidateNs": 122.07715820312437 + }, + { + "baselineNs": 151.28273437500005, + "candidateNs": 119.07432617187297 + }, + { + "baselineNs": 169.55270507812872, + "candidateNs": 119.64169921875546 + }, + { + "baselineNs": 273.3397460937503, + "candidateNs": 216.94490234374973 + }, + { + "baselineNs": 149.42161132812436, + "candidateNs": 124.31273437500501 + }, + { + "baselineNs": 147.00239257813053, + "candidateNs": 120.10722656250474 + }, + { + "baselineNs": 149.32014648437075, + "candidateNs": 119.7609667968802 + }, + { + "baselineNs": 148.5837988281258, + "candidateNs": 119.26569335937387 + }, + { + "baselineNs": 151.40005859374915, + "candidateNs": 120.4606347656334 + }, + { + "baselineNs": 149.3244433593688, + "candidateNs": 124.10286132812587 + }, + { + "baselineNs": 146.8478906250059, + "candidateNs": 119.84090820312154 + }, + { + "baselineNs": 146.6446777343755, + "candidateNs": 120.19724609375615 + }, + { + "baselineNs": 154.2596582031308, + "candidateNs": 131.27648437500028 + }, + { + "baselineNs": 240.52862304687395, + "candidateNs": 247.20266601562457 + }, + { + "baselineNs": 154.96225585938106, + "candidateNs": 184.80418945312493 + }, + { + "baselineNs": 155.35312499999955, + "candidateNs": 120.54663085938165 + }, + { + "baselineNs": 163.4922363281266, + "candidateNs": 122.47009765625094 + }, + { + "baselineNs": 161.0586328125052, + "candidateNs": 121.05071289062685 + } + ] + }, + { + "mode": "prepared", + "name": "connection / 100 rows", + "count": 1600, + "baselineNs": 20627.377499999966, + "candidateNs": 16061.450000000263, + "ratio": 0.7978602752308714, + "samples": [ + { + "baselineNs": 19792.75687499978, + "candidateNs": 15825.009375000149 + }, + { + "baselineNs": 20442.534375000036, + "candidateNs": 16235.904375000133 + }, + { + "baselineNs": 19411.39249999992, + "candidateNs": 15922.431249999818 + }, + { + "baselineNs": 19921.13687500023, + "candidateNs": 15894.283749999546 + }, + { + "baselineNs": 22088.701874999973, + "candidateNs": 16732.956249999803 + }, + { + "baselineNs": 21853.450624999485, + "candidateNs": 15765.38375000041 + }, + { + "baselineNs": 35173.21999999978, + "candidateNs": 17399.35937499979 + }, + { + "baselineNs": 20438.85875000001, + "candidateNs": 16195.784999999889 + }, + { + "baselineNs": 19944.75687500028, + "candidateNs": 16476.20874999973 + }, + { + "baselineNs": 19709.017499999958, + "candidateNs": 15825.166249999826 + }, + { + "baselineNs": 20073.763124999576, + "candidateNs": 15833.381250000117 + }, + { + "baselineNs": 21501.91124999992, + "candidateNs": 16044.85000000011 + }, + { + "baselineNs": 20068.603125000096, + "candidateNs": 16022.620625000172 + }, + { + "baselineNs": 20122.96187499999, + "candidateNs": 16061.450000000263 + }, + { + "baselineNs": 24133.296250000174, + "candidateNs": 27438.598749999983 + }, + { + "baselineNs": 21772.830000000453, + "candidateNs": 18929.32500000029 + }, + { + "baselineNs": 21122.175000000425, + "candidateNs": 16312.711875000334 + }, + { + "baselineNs": 20627.377499999966, + "candidateNs": 16372.988749999422 + }, + { + "baselineNs": 19893.854375000046, + "candidateNs": 15750.431249999792 + }, + { + "baselineNs": 19688.196875000584, + "candidateNs": 16301.516249999395 + }, + { + "baselineNs": 19562.546875000637, + "candidateNs": 16053.459375000331 + }, + { + "baselineNs": 21263.466875000177, + "candidateNs": 16827.979374999133 + }, + { + "baselineNs": 25625.233749999552, + "candidateNs": 25746.213125000846 + }, + { + "baselineNs": 21296.554374999916, + "candidateNs": 15969.331874999853 + }, + { + "baselineNs": 21001.616249999413, + "candidateNs": 15830.37562500067 + }, + { + "baselineNs": 21021.140624999363, + "candidateNs": 15915.574375000004 + }, + { + "baselineNs": 20868.87812499981, + "candidateNs": 16024.235625000074 + }, + { + "baselineNs": 19349.319374999825, + "candidateNs": 15662.690625000549 + }, + { + "baselineNs": 19805.129999999735, + "candidateNs": 16928.78250000035 + }, + { + "baselineNs": 21146.84062500032, + "candidateNs": 17599.04312499998 + }, + { + "baselineNs": 21598.932499999817, + "candidateNs": 28216.625000000022 + } + ] + }, + { + "mode": "prepared", + "name": "plural fragment / 100 rows", + "count": 3200, + "baselineNs": 14628.395000000295, + "candidateNs": 12268.573437499981, + "ratio": 0.8496155826051577, + "samples": [ + { + "baselineNs": 14479.191250000555, + "candidateNs": 12143.57781250044 + }, + { + "baselineNs": 14913.865624999971, + "candidateNs": 13034.144999999739 + }, + { + "baselineNs": 14346.249687499721, + "candidateNs": 12230.338749999986 + }, + { + "baselineNs": 15792.00031250025, + "candidateNs": 18270.09531249985 + }, + { + "baselineNs": 14313.494062499785, + "candidateNs": 12197.250937499577 + }, + { + "baselineNs": 14617.76562500006, + "candidateNs": 12073.448437500361 + }, + { + "baselineNs": 14639.92937499995, + "candidateNs": 12213.01843750041 + }, + { + "baselineNs": 14479.229062499711, + "candidateNs": 12268.573437499981 + }, + { + "baselineNs": 14542.614062499979, + "candidateNs": 18010.90249999959 + }, + { + "baselineNs": 14525.50062499995, + "candidateNs": 14588.10968749958 + }, + { + "baselineNs": 17255.262187499626, + "candidateNs": 22001.493125000023 + }, + { + "baselineNs": 22177.06562500041, + "candidateNs": 19172.278124999593 + }, + { + "baselineNs": 26595.33281249992, + "candidateNs": 20125.84562500024 + }, + { + "baselineNs": 14628.395000000295, + "candidateNs": 16311.118750000447 + }, + { + "baselineNs": 14586.33124999949, + "candidateNs": 12161.727812500088 + }, + { + "baselineNs": 14408.958750000238, + "candidateNs": 12613.412499999868 + }, + { + "baselineNs": 14715.597500000398, + "candidateNs": 12097.109062499954 + }, + { + "baselineNs": 14423.645625000177, + "candidateNs": 12215.07562499994 + }, + { + "baselineNs": 21461.2784374998, + "candidateNs": 13226.605625000047 + }, + { + "baselineNs": 14313.424999999712, + "candidateNs": 12174.17624999996 + }, + { + "baselineNs": 14648.010312500333, + "candidateNs": 12097.321874999807 + }, + { + "baselineNs": 14405.702500000074, + "candidateNs": 12044.265312499647 + }, + { + "baselineNs": 14383.113124999909, + "candidateNs": 12254.772500000401 + }, + { + "baselineNs": 20846.720312499656, + "candidateNs": 12076.35718749998 + }, + { + "baselineNs": 16352.972812500184, + "candidateNs": 12127.860937500258 + }, + { + "baselineNs": 14479.13812500019, + "candidateNs": 12129.435625000157 + }, + { + "baselineNs": 14486.909062500217, + "candidateNs": 12906.388124999921 + }, + { + "baselineNs": 16013.111875000162, + "candidateNs": 13604.989374999832 + }, + { + "baselineNs": 16310.123124999904, + "candidateNs": 12683.504375000326 + }, + { + "baselineNs": 15663.601249999601, + "candidateNs": 19937.6806250001 + }, + { + "baselineNs": 14753.719374999719, + "candidateNs": 13483.068437499809 + } + ] + }, + { + "mode": "prepared", + "name": "union list / 100 rows", + "count": 1600, + "baselineNs": 21162.050000000363, + "candidateNs": 15568.825624999363, + "ratio": 0.749143054513265, + "samples": [ + { + "baselineNs": 19146.31062500007, + "candidateNs": 15568.825624999363 + }, + { + "baselineNs": 21122.18812500032, + "candidateNs": 15172.457500000291 + }, + { + "baselineNs": 20671.642500000187, + "candidateNs": 22422.2068749998 + }, + { + "baselineNs": 22513.12250000069, + "candidateNs": 23150.558124999632 + }, + { + "baselineNs": 20833.99312499978, + "candidateNs": 15138.286249999737 + }, + { + "baselineNs": 26052.51062499974, + "candidateNs": 15164.849999999888 + }, + { + "baselineNs": 21868.841874999132, + "candidateNs": 15156.621250000626 + }, + { + "baselineNs": 20558.52874999914, + "candidateNs": 15163.415624999743 + }, + { + "baselineNs": 20324.36749999988, + "candidateNs": 15225.858750000041 + }, + { + "baselineNs": 20760.0350000007, + "candidateNs": 15102.218124999352 + }, + { + "baselineNs": 20184.215624999524, + "candidateNs": 20975.447499999973 + }, + { + "baselineNs": 21078.974999999216, + "candidateNs": 27325.152500000062 + }, + { + "baselineNs": 20828.626875000962, + "candidateNs": 15465.336250000519 + }, + { + "baselineNs": 21495.380624999143, + "candidateNs": 15212.332499999093 + }, + { + "baselineNs": 24604.242500000735, + "candidateNs": 20947.413750000123 + }, + { + "baselineNs": 22016.308749999782, + "candidateNs": 20784.436874999985 + }, + { + "baselineNs": 22497.418125000197, + "candidateNs": 16730.84000000017 + }, + { + "baselineNs": 23028.82812500002, + "candidateNs": 21759.46062499975 + }, + { + "baselineNs": 28020.641875000365, + "candidateNs": 28204.87749999984 + }, + { + "baselineNs": 19847.403750000012, + "candidateNs": 17742.645624999794 + }, + { + "baselineNs": 19867.44812500092, + "candidateNs": 15703.586250000399 + }, + { + "baselineNs": 20820.310624999365, + "candidateNs": 15638.249999999516 + }, + { + "baselineNs": 21162.050000000363, + "candidateNs": 15027.996874999872 + }, + { + "baselineNs": 21257.706874999938, + "candidateNs": 15042.368124999257 + }, + { + "baselineNs": 21268.908750000715, + "candidateNs": 15271.187499999995 + }, + { + "baselineNs": 21446.03062500096, + "candidateNs": 15539.138125000136 + }, + { + "baselineNs": 29655.70687500076, + "candidateNs": 24617.467499999748 + }, + { + "baselineNs": 24181.02999999974, + "candidateNs": 15222.752499998933 + }, + { + "baselineNs": 24174.33624999944, + "candidateNs": 16697.570624999116 + }, + { + "baselineNs": 21157.229374999813, + "candidateNs": 15972.50062500052 + }, + { + "baselineNs": 19871.875000000047, + "candidateNs": 15485.079375000623 + } + ] + }, + { + "mode": "prepared", + "name": "custom scalar list", + "count": 3200, + "baselineNs": 12562.147187500159, + "candidateNs": 11697.77593750041, + "ratio": 0.9771056287453004, + "samples": [ + { + "baselineNs": 16881.29281249985, + "candidateNs": 17092.47000000005 + }, + { + "baselineNs": 15612.545624999826, + "candidateNs": 12530.336874999648 + }, + { + "baselineNs": 18303.21093750001, + "candidateNs": 17302.5921874995 + }, + { + "baselineNs": 12956.673749999936, + "candidateNs": 12143.530937499918 + }, + { + "baselineNs": 11887.982499999907, + "candidateNs": 12365.262499999972 + }, + { + "baselineNs": 15013.804687499714, + "candidateNs": 11492.751249999741 + }, + { + "baselineNs": 14586.008750000019, + "candidateNs": 11436.232187500082 + }, + { + "baselineNs": 11767.623750000097, + "candidateNs": 11287.322499999846 + }, + { + "baselineNs": 11417.333750000013, + "candidateNs": 11275.615937499879 + }, + { + "baselineNs": 11450.96281250005, + "candidateNs": 11440.956562499876 + }, + { + "baselineNs": 11412.978749999638, + "candidateNs": 11567.96218750003 + }, + { + "baselineNs": 11925.093125000216, + "candidateNs": 11535.688437500085 + }, + { + "baselineNs": 15404.790312499586, + "candidateNs": 16240.607187500018 + }, + { + "baselineNs": 13510.401249999972, + "candidateNs": 12039.371562499924 + }, + { + "baselineNs": 12562.147187500159, + "candidateNs": 12505.624687499901 + }, + { + "baselineNs": 15173.014687500201, + "candidateNs": 11963.80999999974 + }, + { + "baselineNs": 17733.174374999977, + "candidateNs": 17260.39656250009 + }, + { + "baselineNs": 17526.712499999918, + "candidateNs": 18661.125000000422 + }, + { + "baselineNs": 14729.266874999781, + "candidateNs": 12021.021249999534 + }, + { + "baselineNs": 12276.094062499966, + "candidateNs": 12062.706562500125 + }, + { + "baselineNs": 11218.774062500075, + "candidateNs": 11413.147500000492 + }, + { + "baselineNs": 11370.272812499707, + "candidateNs": 11697.77593750041 + }, + { + "baselineNs": 11998.046562499667, + "candidateNs": 11490.262187500093 + }, + { + "baselineNs": 14933.117812499859, + "candidateNs": 11382.809062500883 + }, + { + "baselineNs": 14374.837812499662, + "candidateNs": 11342.695937499911 + }, + { + "baselineNs": 11475.255937500606, + "candidateNs": 11233.583437500556 + }, + { + "baselineNs": 11390.104062500086, + "candidateNs": 11608.022187499499 + }, + { + "baselineNs": 11405.367500000237, + "candidateNs": 11593.920312500359 + }, + { + "baselineNs": 11743.675312499136, + "candidateNs": 11474.81125000013 + }, + { + "baselineNs": 11790.028437500268, + "candidateNs": 12044.15562500003 + }, + { + "baselineNs": 15054.024375000381, + "candidateNs": 14338.61312499971 + } + ] + }, + { + "mode": "prepared", + "name": "opaque JSON list", + "count": 204800, + "baselineNs": 453.7815917968757, + "candidateNs": 150.1775781250103, + "ratio": 0.3315833786878028, + "samples": [ + { + "baselineNs": 452.55945800782627, + "candidateNs": 150.51562011718644 + }, + { + "baselineNs": 518.677856445322, + "candidateNs": 153.7096435546914 + }, + { + "baselineNs": 445.0161035156164, + "candidateNs": 150.1775781250103 + }, + { + "baselineNs": 442.2883398437527, + "candidateNs": 153.89157714842838 + }, + { + "baselineNs": 444.2954541015531, + "candidateNs": 149.6462011718691 + }, + { + "baselineNs": 516.1865234375007, + "candidateNs": 141.70761718750313 + }, + { + "baselineNs": 448.1833691406223, + "candidateNs": 144.64221679686418 + }, + { + "baselineNs": 459.2237158203183, + "candidateNs": 147.91617187499995 + }, + { + "baselineNs": 456.26651367186355, + "candidateNs": 148.29520996093137 + }, + { + "baselineNs": 511.9599365234429, + "candidateNs": 147.8623144531177 + }, + { + "baselineNs": 453.7815917968757, + "candidateNs": 147.59413085936757 + }, + { + "baselineNs": 447.130986328137, + "candidateNs": 148.21229492188337 + }, + { + "baselineNs": 449.7703027343824, + "candidateNs": 149.54161132813226 + }, + { + "baselineNs": 510.3443847656131, + "candidateNs": 152.52116699219442 + }, + { + "baselineNs": 449.2248828124978, + "candidateNs": 160.91480957031834 + }, + { + "baselineNs": 525.2454394531192, + "candidateNs": 188.54587890626107 + }, + { + "baselineNs": 522.3197460937535, + "candidateNs": 174.7894189453092 + }, + { + "baselineNs": 579.4645410156285, + "candidateNs": 226.95003906250125 + }, + { + "baselineNs": 523.0420556640603, + "candidateNs": 192.31662597656296 + }, + { + "baselineNs": 535.0109619140575, + "candidateNs": 160.61160156251475 + }, + { + "baselineNs": 509.00253906251083, + "candidateNs": 172.4769384765601 + }, + { + "baselineNs": 490.4406591796828, + "candidateNs": 149.83444824219205 + }, + { + "baselineNs": 446.45265624998484, + "candidateNs": 148.20211425780272 + }, + { + "baselineNs": 449.01408691405464, + "candidateNs": 153.78341308593946 + }, + { + "baselineNs": 520.7912207031385, + "candidateNs": 149.05788085938454 + }, + { + "baselineNs": 448.2649658203108, + "candidateNs": 148.6372119140711 + }, + { + "baselineNs": 449.00009277343503, + "candidateNs": 147.7928955078056 + }, + { + "baselineNs": 449.78248535157303, + "candidateNs": 151.11440429688372 + }, + { + "baselineNs": 516.1109912109474, + "candidateNs": 150.75816406248777 + }, + { + "baselineNs": 452.1954931640515, + "candidateNs": 143.58312988282051 + }, + { + "baselineNs": 451.56920410155624, + "candidateNs": 154.2918457031206 + } + ] + }, + { + "mode": "prepared", + "name": "recursive inputs", + "count": 1600, + "baselineNs": 16658.239999999296, + "candidateNs": 14884.5762499991, + "ratio": 0.8893198241028453, + "samples": [ + { + "baselineNs": 16436.871875000634, + "candidateNs": 21028.798749998714 + }, + { + "baselineNs": 16804.923750000853, + "candidateNs": 23802.214375000403 + }, + { + "baselineNs": 16233.94499999904, + "candidateNs": 14689.657500000521 + }, + { + "baselineNs": 16380.315000001247, + "candidateNs": 16722.486874998594 + }, + { + "baselineNs": 16407.59750000143, + "candidateNs": 15206.146250000074 + }, + { + "baselineNs": 16327.916249999817, + "candidateNs": 14860.899999998765 + }, + { + "baselineNs": 16647.83937500033, + "candidateNs": 14809.885000001941 + }, + { + "baselineNs": 16569.904999998926, + "candidateNs": 14735.944999999901 + }, + { + "baselineNs": 16912.99625000056, + "candidateNs": 14802.345624998452 + }, + { + "baselineNs": 16560.380625001017, + "candidateNs": 14567.783124998641 + }, + { + "baselineNs": 24449.93187500131, + "candidateNs": 21363.143124999624 + }, + { + "baselineNs": 16488.68875000062, + "candidateNs": 14502.76062500052 + }, + { + "baselineNs": 16658.239999999296, + "candidateNs": 14635.204999999587 + }, + { + "baselineNs": 16543.022500000006, + "candidateNs": 14884.5762499991 + }, + { + "baselineNs": 16459.983749998628, + "candidateNs": 14558.647500000461 + }, + { + "baselineNs": 17080.343749998974, + "candidateNs": 14468.051874998764 + }, + { + "baselineNs": 17039.09062500088, + "candidateNs": 14628.85500000084 + }, + { + "baselineNs": 16625.509375001002, + "candidateNs": 14766.077500000847 + }, + { + "baselineNs": 16614.357500000096, + "candidateNs": 14744.836875001965 + }, + { + "baselineNs": 23301.167499998883, + "candidateNs": 14738.05562500047 + }, + { + "baselineNs": 24263.96187499904, + "candidateNs": 15031.72312499828 + }, + { + "baselineNs": 16700.96437500206, + "candidateNs": 14925.948125001016 + }, + { + "baselineNs": 17312.71437500027, + "candidateNs": 15064.58500000008 + }, + { + "baselineNs": 16936.616250000043, + "candidateNs": 15007.020000000466 + }, + { + "baselineNs": 16516.87875000107, + "candidateNs": 14909.485624998524 + }, + { + "baselineNs": 16909.46500000109, + "candidateNs": 15449.725625001063 + }, + { + "baselineNs": 16645.12187499895, + "candidateNs": 15170.071874999849 + }, + { + "baselineNs": 22187.563750001118, + "candidateNs": 20489.116875000946 + }, + { + "baselineNs": 25374.68562499953, + "candidateNs": 23534.834375000173 + }, + { + "baselineNs": 17893.743125000583, + "candidateNs": 18115.894374998334 + }, + { + "baselineNs": 16722.787500000322, + "candidateNs": 14772.194999998192 + } + ] + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/paired-summary.json b/packages/rescript-relay/benchmarks/results/js-specialization/paired-summary.json new file mode 100644 index 00000000..f6fff92c --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/paired-summary.json @@ -0,0 +1,210 @@ +[ + { + "fixtureVersion": 3, + "node": "v24.16.0", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "cpuAffinity": "7", + "samples": 31, + "targetSampleMs": 20, + "initialLoad": [ + 22.3, + 18.93, + 19.82 + ], + "finalLoad": [ + 18.66, + 18.35, + 19.61 + ], + "implementations": { + "baseline": "/tmp/relay-js-sweep/baseline/utils.js", + "candidate": "/tmp/rescript-relay-conversion-perf/packages/rescript-relay/src/utils.js" + }, + "sha256": { + "baseline": "7f68600a1480e8bfa22b754383a9734dcbaaccc55f7fbf923c06489864287294", + "preparedBaseline": "da045e26c642da31d9b9808b840b65852bd58fbf683d1f5b3ba6ad80e60b2847", + "candidate": "7f68600a1480e8bfa22b754383a9734dcbaaccc55f7fbf923c06489864287294", + "preparedCandidate": "db39247dc78284162c7fe25551523d1681c4e469d37fceb7e24912b542a2e5c9", + "fixtures": "d660fba22de94a8c016e773830d1c9f1b24fc3ce46bfd2016c5e6d66de57d164" + }, + "results": [ + { + "mode": "prepared", + "name": "small fragment", + "count": 409600, + "baselineNs": 133.1544189453124, + "candidateNs": 107.24046142578158, + "ratio": 0.9096685277863316 + }, + { + "mode": "prepared", + "name": "unchanged object", + "count": 819200, + "baselineNs": 58.568532714844416, + "candidateNs": 50.305067138671774, + "ratio": 0.8502233170869574 + }, + { + "mode": "prepared", + "name": "nullable query", + "count": 204800, + "baselineNs": 153.67666503906196, + "candidateNs": 123.89265136718565, + "ratio": 0.8094886849135811 + }, + { + "mode": "prepared", + "name": "connection / 100 rows", + "count": 1600, + "baselineNs": 20544.051250000164, + "candidateNs": 16301.078125000005, + "ratio": 0.8018581403398429 + }, + { + "mode": "prepared", + "name": "plural fragment / 100 rows", + "count": 3200, + "baselineNs": 14444.88906250001, + "candidateNs": 12310.415312499572, + "ratio": 0.8546198603466574 + }, + { + "mode": "prepared", + "name": "union list / 100 rows", + "count": 1600, + "baselineNs": 17610.5831250004, + "candidateNs": 14483.900000000176, + "ratio": 0.8229835643663055 + }, + { + "mode": "prepared", + "name": "custom scalar list", + "count": 3200, + "baselineNs": 11502.360000000068, + "candidateNs": 11730.23124999986, + "ratio": 0.9985159326394925 + }, + { + "mode": "prepared", + "name": "opaque JSON list", + "count": 204800, + "baselineNs": 457.5926562499966, + "candidateNs": 152.5702294921949, + "ratio": 0.33681605845024465 + }, + { + "mode": "prepared", + "name": "recursive inputs", + "count": 1600, + "baselineNs": 15661.099999999808, + "candidateNs": 14689.069375001509, + "ratio": 0.9428667930457257 + } + ], + "run": 1 + }, + { + "fixtureVersion": 3, + "node": "v24.16.0", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "cpuAffinity": "7", + "samples": 31, + "targetSampleMs": 20, + "initialLoad": [ + 18.66, + 18.35, + 19.61 + ], + "finalLoad": [ + 18.51, + 18.31, + 19.56 + ], + "implementations": { + "baseline": "/tmp/relay-js-sweep/baseline/utils.js", + "candidate": "/tmp/rescript-relay-conversion-perf/packages/rescript-relay/src/utils.js" + }, + "sha256": { + "baseline": "7f68600a1480e8bfa22b754383a9734dcbaaccc55f7fbf923c06489864287294", + "preparedBaseline": "da045e26c642da31d9b9808b840b65852bd58fbf683d1f5b3ba6ad80e60b2847", + "candidate": "7f68600a1480e8bfa22b754383a9734dcbaaccc55f7fbf923c06489864287294", + "preparedCandidate": "db39247dc78284162c7fe25551523d1681c4e469d37fceb7e24912b542a2e5c9", + "fixtures": "d660fba22de94a8c016e773830d1c9f1b24fc3ce46bfd2016c5e6d66de57d164" + }, + "results": [ + { + "mode": "prepared", + "name": "small fragment", + "count": 409600, + "baselineNs": 101.97609375000027, + "candidateNs": 93.63988037109361, + "ratio": 0.911205684427288 + }, + { + "mode": "prepared", + "name": "unchanged object", + "count": 819200, + "baselineNs": 57.85585937500004, + "candidateNs": 49.18847900390655, + "ratio": 0.8518584576886907 + }, + { + "mode": "prepared", + "name": "nullable query", + "count": 102400, + "baselineNs": 150.39659179687524, + "candidateNs": 121.40841796874469, + "ratio": 0.8109976437073325 + }, + { + "mode": "prepared", + "name": "connection / 100 rows", + "count": 1600, + "baselineNs": 20627.377499999966, + "candidateNs": 16061.450000000263, + "ratio": 0.7978602752308714 + }, + { + "mode": "prepared", + "name": "plural fragment / 100 rows", + "count": 3200, + "baselineNs": 14628.395000000295, + "candidateNs": 12268.573437499981, + "ratio": 0.8496155826051577 + }, + { + "mode": "prepared", + "name": "union list / 100 rows", + "count": 1600, + "baselineNs": 21162.050000000363, + "candidateNs": 15568.825624999363, + "ratio": 0.749143054513265 + }, + { + "mode": "prepared", + "name": "custom scalar list", + "count": 3200, + "baselineNs": 12562.147187500159, + "candidateNs": 11697.77593750041, + "ratio": 0.9771056287453004 + }, + { + "mode": "prepared", + "name": "opaque JSON list", + "count": 204800, + "baselineNs": 453.7815917968757, + "candidateNs": 150.1775781250103, + "ratio": 0.3315833786878028 + }, + { + "mode": "prepared", + "name": "recursive inputs", + "count": 1600, + "baselineNs": 16658.239999999296, + "candidateNs": 14884.5762499991, + "ratio": 0.8893198241028453 + } + ], + "run": 2 + } +] diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/runtime-size-esm.json b/packages/rescript-relay/benchmarks/results/js-specialization/runtime-size-esm.json new file mode 100644 index 00000000..70315233 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/runtime-size-esm.json @@ -0,0 +1,15 @@ +{ + "esbuild": "0.25.12", + "results": { + "before": { + "minified": 5578, + "gzip": 2238, + "brotli": 2032 + }, + "after": { + "minified": 5847, + "gzip": 2320, + "brotli": 2101 + } + } +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/runtime-size.json b/packages/rescript-relay/benchmarks/results/js-specialization/runtime-size.json new file mode 100644 index 00000000..032bab0a --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/runtime-size.json @@ -0,0 +1,15 @@ +{ + "esbuild": "0.25.12", + "results": { + "before": { + "minified": 5786, + "gzip": 2330, + "brotli": 2108 + }, + "after": { + "minified": 6056, + "gzip": 2409, + "brotli": 2178 + } + } +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/standalone-after.json b/packages/rescript-relay/benchmarks/results/js-specialization/standalone-after.json new file mode 100644 index 00000000..9585ed74 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/standalone-after.json @@ -0,0 +1,670 @@ +{ + "fixtureVersion": 3, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "cpuTiming": "thread", + "implementation": "src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 819200, + "newOutputObjects": 3, + "preparationNs": 5949.421999999998, + "medianNs": 95.10610595703115, + "medianCpuNs": 95.189208984375, + "cpuSamples": [ + { + "wallNs": 91.39721801757808, + "cpuNs": 91.50634765625 + }, + { + "wallNs": 102.88083129882808, + "cpuNs": 102.509765625 + }, + { + "wallNs": 102.97089355468754, + "cpuNs": 103.714599609375 + }, + { + "wallNs": 87.85980834960947, + "cpuNs": 87.8662109375 + }, + { + "wallNs": 95.10610595703115, + "cpuNs": 95.189208984375 + }, + { + "wallNs": 86.03745483398443, + "cpuNs": 85.426025390625 + }, + { + "wallNs": 91.04812011718755, + "cpuNs": 91.53076171875 + }, + { + "wallNs": 127.2467578125, + "cpuNs": 126.90185546875 + }, + { + "wallNs": 118.41307373046881, + "cpuNs": 118.372802734375 + }, + { + "wallNs": 166.60675292968747, + "cpuNs": 167.203369140625 + }, + { + "wallNs": 124.94414062499987, + "cpuNs": 124.47021484375 + }, + { + "wallNs": 86.66969238281251, + "cpuNs": 86.63818359375 + }, + { + "wallNs": 87.64365234375016, + "cpuNs": 87.867431640625 + }, + { + "wallNs": 116.43067871093758, + "cpuNs": 115.931396484375 + }, + { + "wallNs": 92.27530395507806, + "cpuNs": 92.750244140625 + } + ], + "p10Ns": 86.66969238281251, + "p90Ns": 127.2467578125, + "opsPerSecond": 10514572.013407836 + }, + { + "name": "unchanged object", + "iterations": 1638400, + "newOutputObjects": 0, + "preparationNs": 0, + "medianNs": 47.22500976562502, + "medianCpuNs": 46.9970703125, + "cpuSamples": [ + { + "wallNs": 46.02683532714841, + "cpuNs": 45.7696533203125 + }, + { + "wallNs": 46.664110717773426, + "cpuNs": 46.38671875 + }, + { + "wallNs": 62.069307250976614, + "cpuNs": 62.2369384765625 + }, + { + "wallNs": 49.25921264648442, + "cpuNs": 49.4384765625 + }, + { + "wallNs": 47.22500976562502, + "cpuNs": 46.9970703125 + }, + { + "wallNs": 51.43985900878917, + "cpuNs": 51.8682861328125 + }, + { + "wallNs": 49.060185546875026, + "cpuNs": 48.8238525390625 + }, + { + "wallNs": 46.39650939941398, + "cpuNs": 46.37939453125 + }, + { + "wallNs": 59.808210449218684, + "cpuNs": 59.814453125 + }, + { + "wallNs": 46.75909057617195, + "cpuNs": 46.9964599609375 + }, + { + "wallNs": 47.758562011718524, + "cpuNs": 47.6068115234375 + }, + { + "wallNs": 45.912666625976534, + "cpuNs": 45.767822265625 + }, + { + "wallNs": 46.489287109375, + "cpuNs": 46.3836669921875 + }, + { + "wallNs": 46.224210815429544, + "cpuNs": 46.38671875 + }, + { + "wallNs": 60.61259338378893, + "cpuNs": 60.418701171875 + } + ], + "p10Ns": 46.02683532714841, + "p90Ns": 60.61259338378893, + "opsPerSecond": 21175220.60795629 + }, + { + "name": "nullable query", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 0, + "medianNs": 115.87322753906281, + "medianCpuNs": 117.13134765625, + "cpuSamples": [ + { + "wallNs": 119.68656005859391, + "cpuNs": 119.57275390625 + }, + { + "wallNs": 116.14207031250045, + "cpuNs": 117.1142578125 + }, + { + "wallNs": 115.87322753906281, + "cpuNs": 114.66796875 + }, + { + "wallNs": 119.18338867187516, + "cpuNs": 119.560546875 + }, + { + "wallNs": 115.5820532226559, + "cpuNs": 117.158203125 + }, + { + "wallNs": 115.3517822265626, + "cpuNs": 114.67529296875 + }, + { + "wallNs": 222.09853759765585, + "cpuNs": 222.099609375 + }, + { + "wallNs": 157.1183764648443, + "cpuNs": 156.14990234375 + }, + { + "wallNs": 115.83614746093707, + "cpuNs": 117.13623046875 + }, + { + "wallNs": 115.13019775390654, + "cpuNs": 114.70458984375 + }, + { + "wallNs": 116.91423339843743, + "cpuNs": 117.13134765625 + }, + { + "wallNs": 115.06711425781192, + "cpuNs": 114.6484375 + }, + { + "wallNs": 118.86388671874971, + "cpuNs": 119.57763671875 + }, + { + "wallNs": 115.5432592773442, + "cpuNs": 114.67529296875 + }, + { + "wallNs": 115.74750244140674, + "cpuNs": 114.69970703125 + } + ], + "p10Ns": 115.13019775390654, + "p90Ns": 157.1183764648443, + "opsPerSecond": 8630121.221598692 + }, + { + "name": "connection / 100 rows", + "iterations": 3200, + "newOutputObjects": 403, + "preparationNs": 5694.891000000098, + "medianNs": 15765.46250000007, + "medianCpuNs": 15586.25, + "cpuSamples": [ + { + "wallNs": 17901.758750000026, + "cpuNs": 18070 + }, + { + "wallNs": 21025.360937499896, + "cpuNs": 20560 + }, + { + "wallNs": 19115.433437500116, + "cpuNs": 18952.8125 + }, + { + "wallNs": 15449.938125000244, + "cpuNs": 15575.3125 + }, + { + "wallNs": 15476.507187500203, + "cpuNs": 15457.5 + }, + { + "wallNs": 15995.146250000118, + "cpuNs": 15707.5 + }, + { + "wallNs": 15215.81124999983, + "cpuNs": 15269.6875 + }, + { + "wallNs": 22682.1884375002, + "cpuNs": 22757.5 + }, + { + "wallNs": 15656.099374999998, + "cpuNs": 15579.6875 + }, + { + "wallNs": 15302.963437499953, + "cpuNs": 15277.1875 + }, + { + "wallNs": 15765.46250000007, + "cpuNs": 15586.25 + }, + { + "wallNs": 17238.326874999926, + "cpuNs": 17140 + }, + { + "wallNs": 15338.624687499874, + "cpuNs": 15583.4375 + }, + { + "wallNs": 15865.160312499995, + "cpuNs": 15830.625 + }, + { + "wallNs": 15591.223750000154, + "cpuNs": 15329.375 + } + ], + "p10Ns": 15302.963437499953, + "p90Ns": 21025.360937499896, + "opsPerSecond": 63429.791545918524 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 6400, + "newOutputObjects": 301, + "preparationNs": 1207.331999999951, + "medianNs": 12846.041250000015, + "medianCpuNs": 12836.09375, + "cpuSamples": [ + { + "wallNs": 12379.555156250035, + "cpuNs": 12311.875 + }, + { + "wallNs": 12846.041250000015, + "cpuNs": 12874.21875 + }, + { + "wallNs": 12916.93140625, + "cpuNs": 12836.09375 + }, + { + "wallNs": 14356.345156249972, + "cpuNs": 14337.8125 + }, + { + "wallNs": 14429.54125, + "cpuNs": 14335.625 + }, + { + "wallNs": 16713.045156249962, + "cpuNs": 16677.5 + }, + { + "wallNs": 12211.506249999928, + "cpuNs": 12324.6875 + }, + { + "wallNs": 12052.627968750045, + "cpuNs": 12013.90625 + }, + { + "wallNs": 12002.675781250076, + "cpuNs": 12010.625 + }, + { + "wallNs": 12236.741406250074, + "cpuNs": 12169.0625 + }, + { + "wallNs": 16317.635624999979, + "cpuNs": 16223.28125 + }, + { + "wallNs": 18144.185468750038, + "cpuNs": 18099.84375 + }, + { + "wallNs": 12126.77421875, + "cpuNs": 12165.3125 + }, + { + "wallNs": 12177.232187499954, + "cpuNs": 12166.875 + }, + { + "wallNs": 19870.90609375002, + "cpuNs": 19815.15625 + } + ], + "p10Ns": 12052.627968750045, + "p90Ns": 18144.185468750038, + "opsPerSecond": 77844.99368628439 + }, + { + "name": "union list / 100 rows", + "iterations": 3200, + "newOutputObjects": 302, + "preparationNs": 5832.9219999995985, + "medianNs": 30800.429062500145, + "medianCpuNs": 30579.375, + "cpuSamples": [ + { + "wallNs": 30800.429062500145, + "cpuNs": 30583.75 + }, + { + "wallNs": 30348.572187500056, + "cpuNs": 30576.5625 + }, + { + "wallNs": 30433.231874999936, + "cpuNs": 30260.3125 + }, + { + "wallNs": 30136.900312500075, + "cpuNs": 29951.875 + }, + { + "wallNs": 30332.607500000107, + "cpuNs": 30264.0625 + }, + { + "wallNs": 30355.131249999944, + "cpuNs": 30579.375 + }, + { + "wallNs": 30488.749375000167, + "cpuNs": 30260.625 + }, + { + "wallNs": 30822.661874999823, + "cpuNs": 30891.25 + }, + { + "wallNs": 31028.77937500011, + "cpuNs": 30893.4375 + }, + { + "wallNs": 30892.875625000048, + "cpuNs": 30887.1875 + }, + { + "wallNs": 31476.080937499944, + "cpuNs": 31517.5 + }, + { + "wallNs": 31222.063437500085, + "cpuNs": 31206.5625 + }, + { + "wallNs": 31505.649062499968, + "cpuNs": 31518.125 + }, + { + "wallNs": 30807.210312500218, + "cpuNs": 30574.0625 + }, + { + "wallNs": 30600.23812499992, + "cpuNs": 30571.875 + } + ], + "p10Ns": 30332.607500000107, + "p90Ns": 31476.080937499944, + "opsPerSecond": 32467.080181604055 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "preparationNs": 4275.648999999248, + "medianNs": 21663.08500000014, + "medianCpuNs": 21547.8125, + "cpuSamples": [ + { + "wallNs": 21822.62687500014, + "cpuNs": 21835.9375 + }, + { + "wallNs": 21545.94156249999, + "cpuNs": 21545.625 + }, + { + "wallNs": 21412.536562500292, + "cpuNs": 21229.375 + }, + { + "wallNs": 21524.992812500157, + "cpuNs": 21542.5 + }, + { + "wallNs": 21399.34593750013, + "cpuNs": 21503.125 + }, + { + "wallNs": 21663.08500000014, + "cpuNs": 21419.375 + }, + { + "wallNs": 21585.315937500127, + "cpuNs": 21546.25 + }, + { + "wallNs": 21584.91187499976, + "cpuNs": 21546.875 + }, + { + "wallNs": 21759.98687499998, + "cpuNs": 21858.4375 + }, + { + "wallNs": 22043.59124999996, + "cpuNs": 21866.5625 + }, + { + "wallNs": 22210.816875000317, + "cpuNs": 22165.625 + }, + { + "wallNs": 22080.398437499865, + "cpuNs": 22168.4375 + }, + { + "wallNs": 22160.38718749985, + "cpuNs": 22172.8125 + }, + { + "wallNs": 22125.026562500236, + "cpuNs": 22172.1875 + }, + { + "wallNs": 21589.379999999775, + "cpuNs": 21547.8125 + } + ], + "p10Ns": 21412.536562500292, + "p90Ns": 22160.38718749985, + "opsPerSecond": 46161.477001082414 + }, + { + "name": "opaque JSON list", + "iterations": 409600, + "newOutputObjects": 0, + "preparationNs": 3253.177999999025, + "medianNs": 220.89814941406158, + "medianCpuNs": 222.0654296875, + "cpuSamples": [ + { + "wallNs": 157.09154541015735, + "cpuNs": 158.68896484375 + }, + { + "wallNs": 159.77429931640864, + "cpuNs": 158.68896484375 + }, + { + "wallNs": 187.8418627929701, + "cpuNs": 182.6806640625 + }, + { + "wallNs": 157.0997607421898, + "cpuNs": 156.25 + }, + { + "wallNs": 158.20108642578214, + "cpuNs": 158.68896484375 + }, + { + "wallNs": 194.64226074218604, + "cpuNs": 195.3125 + }, + { + "wallNs": 228.90872070312707, + "cpuNs": 229.462890625 + }, + { + "wallNs": 226.4634497070306, + "cpuNs": 227.00439453125 + }, + { + "wallNs": 223.1860913085937, + "cpuNs": 222.0654296875 + }, + { + "wallNs": 224.61572265624952, + "cpuNs": 224.609375 + }, + { + "wallNs": 220.71117675781517, + "cpuNs": 219.6728515625 + }, + { + "wallNs": 220.89814941406158, + "cpuNs": 222.16552734375 + }, + { + "wallNs": 224.30262939453272, + "cpuNs": 224.5947265625 + }, + { + "wallNs": 223.057966308593, + "cpuNs": 222.13623046875 + }, + { + "wallNs": 223.35139404296987, + "cpuNs": 224.58251953125 + } + ], + "p10Ns": 157.0997607421898, + "p90Ns": 226.4634497070306, + "opsPerSecond": 4526973.189465496 + }, + { + "name": "recursive inputs", + "iterations": 3200, + "newOutputObjects": 103, + "preparationNs": 5699.339000000691, + "medianNs": 31886.305937500194, + "medianCpuNs": 31849.0625, + "cpuSamples": [ + { + "wallNs": 15432.214062500407, + "cpuNs": 15488.4375 + }, + { + "wallNs": 15597.576562499853, + "cpuNs": 15359.0625 + }, + { + "wallNs": 15467.643437499986, + "cpuNs": 15607.5 + }, + { + "wallNs": 25675.920937499657, + "cpuNs": 25591.5625 + }, + { + "wallNs": 32154.781874999684, + "cpuNs": 32164.6875 + }, + { + "wallNs": 31995.070937500146, + "cpuNs": 31851.5625 + }, + { + "wallNs": 31886.305937500194, + "cpuNs": 31865.9375 + }, + { + "wallNs": 31936.529062500085, + "cpuNs": 31849.0625 + }, + { + "wallNs": 32218.85312500035, + "cpuNs": 32162.5 + }, + { + "wallNs": 32268.133437499953, + "cpuNs": 32164.6875 + }, + { + "wallNs": 32171.450937500478, + "cpuNs": 32161.5625 + }, + { + "wallNs": 32284.257812499956, + "cpuNs": 32165.3125 + }, + { + "wallNs": 22717.542812499687, + "cpuNs": 22787.8125 + }, + { + "wallNs": 16489.073750000218, + "cpuNs": 15457.5 + }, + { + "wallNs": 15698.952187499914, + "cpuNs": 15914.375 + } + ], + "p10Ns": 15467.643437499986, + "p90Ns": 32268.133437499953, + "opsPerSecond": 31361.42524505921 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/js-specialization/standalone-before.json b/packages/rescript-relay/benchmarks/results/js-specialization/standalone-before.json new file mode 100644 index 00000000..4bd9e261 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/js-specialization/standalone-before.json @@ -0,0 +1,670 @@ +{ + "fixtureVersion": 3, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "cpuTiming": "thread", + "implementation": "/tmp/relay-js-sweep/baseline/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 7002.310999999999, + "medianNs": 100.9031176757813, + "medianCpuNs": 100.0634765625, + "cpuSamples": [ + { + "wallNs": 100.17481445312497, + "cpuNs": 100.0634765625 + }, + { + "wallNs": 99.9192041015625, + "cpuNs": 100.0390625 + }, + { + "wallNs": 100.9031176757813, + "cpuNs": 100.01220703125 + }, + { + "wallNs": 102.31249511718748, + "cpuNs": 102.49755859375 + }, + { + "wallNs": 187.80910888671883, + "cpuNs": 187.88818359375 + }, + { + "wallNs": 143.3190356445313, + "cpuNs": 143.94775390625 + }, + { + "wallNs": 98.3079565429687, + "cpuNs": 97.6025390625 + }, + { + "wallNs": 97.4653002929686, + "cpuNs": 97.61474609375 + }, + { + "wallNs": 206.51361816406254, + "cpuNs": 124.75341796875 + }, + { + "wallNs": 96.48194580078123, + "cpuNs": 95.166015625 + }, + { + "wallNs": 101.0023291015627, + "cpuNs": 100.0634765625 + }, + { + "wallNs": 97.71158935546892, + "cpuNs": 97.60498046875 + }, + { + "wallNs": 97.98104492187514, + "cpuNs": 97.60498046875 + }, + { + "wallNs": 103.14610107421879, + "cpuNs": 102.5 + }, + { + "wallNs": 116.14500732421874, + "cpuNs": 117.12890625 + } + ], + "p10Ns": 97.4653002929686, + "p90Ns": 187.80910888671883, + "opsPerSecond": 9910496.553864354 + }, + { + "name": "unchanged object", + "iterations": 819200, + "newOutputObjects": 0, + "preparationNs": 0, + "medianNs": 69.9711596679689, + "medianCpuNs": 69.580078125, + "cpuSamples": [ + { + "wallNs": 60.792462158203165, + "cpuNs": 61.00341796875 + }, + { + "wallNs": 84.81014892578132, + "cpuNs": 84.228515625 + }, + { + "wallNs": 52.806717529296776, + "cpuNs": 53.7109375 + }, + { + "wallNs": 53.400614013671685, + "cpuNs": 52.489013671875 + }, + { + "wallNs": 70.32150268554693, + "cpuNs": 70.7861328125 + }, + { + "wallNs": 53.33789672851558, + "cpuNs": 53.712158203125 + }, + { + "wallNs": 71.59639770507792, + "cpuNs": 70.694580078125 + }, + { + "wallNs": 65.36670532226563, + "cpuNs": 65.91064453125 + }, + { + "wallNs": 57.56339843750013, + "cpuNs": 57.364501953125 + }, + { + "wallNs": 69.9711596679689, + "cpuNs": 70.80078125 + }, + { + "wallNs": 70.57305297851562, + "cpuNs": 69.580078125 + }, + { + "wallNs": 70.86381225585941, + "cpuNs": 70.787353515625 + }, + { + "wallNs": 69.97182006835939, + "cpuNs": 70.799560546875 + }, + { + "wallNs": 52.875426025390524, + "cpuNs": 52.490234375 + }, + { + "wallNs": 71.01458618164081, + "cpuNs": 70.802001953125 + } + ], + "p10Ns": 52.875426025390524, + "p90Ns": 71.59639770507792, + "opsPerSecond": 14291602.49373108 + }, + { + "name": "nullable query", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 0, + "medianNs": 182.95464843749997, + "medianCpuNs": 183.02734375, + "cpuSamples": [ + { + "wallNs": 183.9224462890626, + "cpuNs": 185.4541015625 + }, + { + "wallNs": 209.4647021484369, + "cpuNs": 209.76806640625 + }, + { + "wallNs": 147.12913818359374, + "cpuNs": 146.44775390625 + }, + { + "wallNs": 148.01995849609395, + "cpuNs": 148.876953125 + }, + { + "wallNs": 149.02874999999983, + "cpuNs": 148.84033203125 + }, + { + "wallNs": 187.51871826171885, + "cpuNs": 185.45166015625 + }, + { + "wallNs": 187.11448730468706, + "cpuNs": 187.94677734375 + }, + { + "wallNs": 182.95464843749997, + "cpuNs": 183.02734375 + }, + { + "wallNs": 218.54770996093785, + "cpuNs": 219.677734375 + }, + { + "wallNs": 218.20578369140532, + "cpuNs": 217.20703125 + }, + { + "wallNs": 151.02554199218798, + "cpuNs": 151.31591796875 + }, + { + "wallNs": 151.0672460937501, + "cpuNs": 151.2890625 + }, + { + "wallNs": 188.95918457031203, + "cpuNs": 187.92236328125 + }, + { + "wallNs": 158.67290283203107, + "cpuNs": 158.64013671875 + }, + { + "wallNs": 171.3014038085936, + "cpuNs": 170.80810546875 + } + ], + "p10Ns": 148.01995849609395, + "p90Ns": 218.20578369140532, + "opsPerSecond": 5465835.432662509 + }, + { + "name": "connection / 100 rows", + "iterations": 3200, + "newOutputObjects": 403, + "preparationNs": 7229.080000000067, + "medianNs": 20237.900937500086, + "medianCpuNs": 20254.6875, + "cpuSamples": [ + { + "wallNs": 20237.900937500086, + "cpuNs": 20254.6875 + }, + { + "wallNs": 21030.683749999924, + "cpuNs": 20518.4375 + }, + { + "wallNs": 19677.73937499999, + "cpuNs": 19633.125 + }, + { + "wallNs": 20192.674999999977, + "cpuNs": 20248.75 + }, + { + "wallNs": 19516.2628125, + "cpuNs": 19315 + }, + { + "wallNs": 26901.72125000004, + "cpuNs": 26813.125 + }, + { + "wallNs": 19468.885624999926, + "cpuNs": 19645.3125 + }, + { + "wallNs": 19562.44656249993, + "cpuNs": 19332.8125 + }, + { + "wallNs": 20891.943437499947, + "cpuNs": 20894.0625 + }, + { + "wallNs": 19795.85656249995, + "cpuNs": 19956.5625 + }, + { + "wallNs": 19517.114062500128, + "cpuNs": 19325.9375 + }, + { + "wallNs": 21185.42593750007, + "cpuNs": 21201.5625 + }, + { + "wallNs": 25085.808749999924, + "cpuNs": 25040 + }, + { + "wallNs": 26355.498749999955, + "cpuNs": 26393.125 + }, + { + "wallNs": 22910.294687499972, + "cpuNs": 22764.6875 + } + ], + "p10Ns": 19516.2628125, + "p90Ns": 26355.498749999955, + "opsPerSecond": 49412.23909971003 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 3200, + "newOutputObjects": 301, + "preparationNs": 1242.557999999917, + "medianNs": 16496.158749999948, + "medianCpuNs": 16213.125, + "cpuSamples": [ + { + "wallNs": 15143.844062500024, + "cpuNs": 15272.5 + }, + { + "wallNs": 16175.5309374999, + "cpuNs": 15630.9375 + }, + { + "wallNs": 17959.069999999996, + "cpuNs": 17768.75 + }, + { + "wallNs": 24509.291249999875, + "cpuNs": 24639.0625 + }, + { + "wallNs": 18856.784999999833, + "cpuNs": 18696.25 + }, + { + "wallNs": 19582.756874999915, + "cpuNs": 19643.4375 + }, + { + "wallNs": 16208.396562500127, + "cpuNs": 16210.9375 + }, + { + "wallNs": 16496.158749999948, + "cpuNs": 16210.9375 + }, + { + "wallNs": 16212.353749999977, + "cpuNs": 16213.125 + }, + { + "wallNs": 16152.3465625001, + "cpuNs": 16207.8125 + }, + { + "wallNs": 16135.78093749993, + "cpuNs": 16210.3125 + }, + { + "wallNs": 16518.050312500065, + "cpuNs": 16195.625 + }, + { + "wallNs": 21392.094375, + "cpuNs": 21514.375 + }, + { + "wallNs": 19758.113125000138, + "cpuNs": 19655.3125 + }, + { + "wallNs": 16334.637812500148, + "cpuNs": 16222.5 + } + ], + "p10Ns": 16135.78093749993, + "p90Ns": 21392.094375, + "opsPerSecond": 60620.173166071356 + }, + { + "name": "union list / 100 rows", + "iterations": 3200, + "newOutputObjects": 302, + "preparationNs": 6242.906999999833, + "medianNs": 17636.18812499999, + "medianCpuNs": 17470.9375, + "cpuSamples": [ + { + "wallNs": 16934.89718749987, + "cpuNs": 16840.3125 + }, + { + "wallNs": 17296.815625000192, + "cpuNs": 17152.5 + }, + { + "wallNs": 16876.161250000052, + "cpuNs": 16845 + }, + { + "wallNs": 17071.668124999916, + "cpuNs": 17146.5625 + }, + { + "wallNs": 23875.08968750012, + "cpuNs": 23700.625 + }, + { + "wallNs": 17056.733750000036, + "cpuNs": 16845.625 + }, + { + "wallNs": 16854.845937500046, + "cpuNs": 16840.625 + }, + { + "wallNs": 17066.257812500113, + "cpuNs": 17162.1875 + }, + { + "wallNs": 17636.18812499999, + "cpuNs": 17470.9375 + }, + { + "wallNs": 18899.017812499893, + "cpuNs": 19024.6875 + }, + { + "wallNs": 18752.870000000145, + "cpuNs": 18721.25 + }, + { + "wallNs": 18918.050937500084, + "cpuNs": 18725.625 + }, + { + "wallNs": 24665.09812499993, + "cpuNs": 24620.625 + }, + { + "wallNs": 19587.788125000145, + "cpuNs": 19652.1875 + }, + { + "wallNs": 21539.792500000203, + "cpuNs": 21521.25 + } + ], + "p10Ns": 16876.161250000052, + "p90Ns": 23875.08968750012, + "opsPerSecond": 56701.59520369715 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "preparationNs": 3283.0439999997902, + "medianNs": 11794.435312499872, + "medianCpuNs": 11864.21875, + "cpuSamples": [ + { + "wallNs": 11269.991093749923, + "cpuNs": 11242.96875 + }, + { + "wallNs": 11761.336874999985, + "cpuNs": 11708.59375 + }, + { + "wallNs": 14540.446093750035, + "cpuNs": 14523.90625 + }, + { + "wallNs": 11434.260937499941, + "cpuNs": 11556.71875 + }, + { + "wallNs": 11408.424687500086, + "cpuNs": 11400.3125 + }, + { + "wallNs": 11508.44656249987, + "cpuNs": 11399.0625 + }, + { + "wallNs": 11655.017187499938, + "cpuNs": 11710.625 + }, + { + "wallNs": 11660.784375000048, + "cpuNs": 11554.0625 + }, + { + "wallNs": 17832.357187500063, + "cpuNs": 12919.84375 + }, + { + "wallNs": 11973.019687500111, + "cpuNs": 11864.21875 + }, + { + "wallNs": 11794.435312499872, + "cpuNs": 11868.28125 + }, + { + "wallNs": 12997.612031250013, + "cpuNs": 12959.0625 + }, + { + "wallNs": 11876.73453125001, + "cpuNs": 11866.875 + }, + { + "wallNs": 11826.940625000048, + "cpuNs": 11867.34375 + }, + { + "wallNs": 13264.289374999977, + "cpuNs": 13274.6875 + } + ], + "p10Ns": 11408.424687500086, + "p90Ns": 14540.446093750035, + "opsPerSecond": 84785.74628665681 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 0, + "preparationNs": 3467.5820000002204, + "medianNs": 443.1474316406181, + "medianCpuNs": 449.169921875, + "cpuSamples": [ + { + "wallNs": 449.1951855468823, + "cpuNs": 449.21875 + }, + { + "wallNs": 447.25460937499406, + "cpuNs": 449.208984375 + }, + { + "wallNs": 449.8569824218723, + "cpuNs": 449.228515625 + }, + { + "wallNs": 454.07648437500114, + "cpuNs": 449.267578125 + }, + { + "wallNs": 441.98831054687025, + "cpuNs": 449.0625 + }, + { + "wallNs": 438.72952148436895, + "cpuNs": 429.677734375 + }, + { + "wallNs": 456.7929492187517, + "cpuNs": 458.88671875 + }, + { + "wallNs": 432.84623046875305, + "cpuNs": 439.443359375 + }, + { + "wallNs": 586.6746093749953, + "cpuNs": 585.64453125 + }, + { + "wallNs": 436.51195312500056, + "cpuNs": 429.697265625 + }, + { + "wallNs": 437.02776367188, + "cpuNs": 439.287109375 + }, + { + "wallNs": 442.2229394531385, + "cpuNs": 449.208984375 + }, + { + "wallNs": 441.343242187493, + "cpuNs": 439.287109375 + }, + { + "wallNs": 443.1474316406181, + "cpuNs": 439.35546875 + }, + { + "wallNs": 446.1407812499907, + "cpuNs": 449.169921875 + } + ], + "p10Ns": 436.51195312500056, + "p90Ns": 456.7929492187517, + "opsPerSecond": 2256585.345192694 + }, + { + "name": "recursive inputs", + "iterations": 3200, + "newOutputObjects": 103, + "preparationNs": 4607.867999999143, + "medianNs": 16817.159062499628, + "medianCpuNs": 16863.125, + "cpuSamples": [ + { + "wallNs": 18069.372187500223, + "cpuNs": 18101.5625 + }, + { + "wallNs": 21393.631875000097, + "cpuNs": 21244.0625 + }, + { + "wallNs": 17263.324062499803, + "cpuNs": 17171.875 + }, + { + "wallNs": 16718.081249999843, + "cpuNs": 16860.625 + }, + { + "wallNs": 16496.603437499856, + "cpuNs": 16547.8125 + }, + { + "wallNs": 16527.840625000183, + "cpuNs": 16547.8125 + }, + { + "wallNs": 16697.06656249957, + "cpuNs": 16548.75 + }, + { + "wallNs": 16475.88312500034, + "cpuNs": 16549.375 + }, + { + "wallNs": 16530.379687499706, + "cpuNs": 16549.6875 + }, + { + "wallNs": 30263.141874999634, + "cpuNs": 18477.1875 + }, + { + "wallNs": 16378.326562499979, + "cpuNs": 16232.5 + }, + { + "wallNs": 21531.31374999987, + "cpuNs": 21537.5 + }, + { + "wallNs": 16817.159062499628, + "cpuNs": 16863.125 + }, + { + "wallNs": 17512.75468750009, + "cpuNs": 17484.6875 + }, + { + "wallNs": 19468.716249999718, + "cpuNs": 19355.9375 + } + ], + "p10Ns": 16475.88312500034, + "p90Ns": 21531.31374999987, + "opsPerSecond": 59463.075557743134 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/optimized.json b/packages/rescript-relay/benchmarks/results/optimized.json new file mode 100644 index 00000000..e770a1e1 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/optimized.json @@ -0,0 +1,94 @@ +{ + "fixtureVersion": 1, + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "working-tree src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "medianNs": 258.71845703125007, + "p10Ns": 243.4020849609375, + "p90Ns": 286.2405078124999, + "opsPerSecond": 3865205.488138838 + }, + { + "name": "unchanged object", + "iterations": 409600, + "newOutputObjects": 0, + "medianNs": 107.08943603515652, + "p10Ns": 102.9204467773437, + "p90Ns": 121.46514648437524, + "opsPerSecond": 9337989.226796458 + }, + { + "name": "nullable query", + "iterations": 102400, + "newOutputObjects": 3, + "medianNs": 427.823339843747, + "p10Ns": 385.3941406249994, + "p90Ns": 486.95416015624994, + "opsPerSecond": 2337413.382741642 + }, + { + "name": "connection / 100 rows", + "iterations": 800, + "newOutputObjects": 403, + "medianNs": 67429.24499999959, + "p10Ns": 64157.26374999963, + "p90Ns": 73941.97374999976, + "opsPerSecond": 14830.360328074355 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 1600, + "newOutputObjects": 301, + "medianNs": 30763.08374999996, + "p10Ns": 27986.07250000003, + "p90Ns": 35886.9681249999, + "opsPerSecond": 32506.494086438954 + }, + { + "name": "union list / 100 rows", + "iterations": 800, + "newOutputObjects": 302, + "medianNs": 62561.63125000058, + "p10Ns": 57967.87124999924, + "p90Ns": 68423.35625000033, + "opsPerSecond": 15984.237942964295 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "medianNs": 11251.709062500198, + "p10Ns": 10571.279374999845, + "p90Ns": 12130.349999999908, + "opsPerSecond": 88875.38723630967 + }, + { + "name": "opaque JSON list", + "iterations": 204800, + "newOutputObjects": 0, + "medianNs": 349.4462451171865, + "p10Ns": 341.903315429688, + "p90Ns": 368.32323730468636, + "opsPerSecond": 2861670.4685570477 + }, + { + "name": "recursive inputs", + "iterations": 1600, + "newOutputObjects": 103, + "medianNs": 30618.235625000238, + "p10Ns": 27549.50000000008, + "p90Ns": 34586.33500000019, + "opsPerSecond": 32660.27514607946 + } + ], + "baseRevision": "520cac4933c118a85cf0dffcd1a19a5dba34ad4f", + "recordedDate": "2026-09-21" +} diff --git a/packages/rescript-relay/benchmarks/results/pr-673.json b/packages/rescript-relay/benchmarks/results/pr-673.json new file mode 100644 index 00000000..c2674a29 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/pr-673.json @@ -0,0 +1,92 @@ +{ + "fixtureVersion": 1, + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "/tmp/relay-conversion-audit/pr-673-utils.cjs", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "medianNs": 342.3455322265623, + "p10Ns": 325.91325683593766, + "p90Ns": 418.4350732421876, + "opsPerSecond": 2921025.413990815 + }, + { + "name": "unchanged object", + "iterations": 204800, + "newOutputObjects": 1, + "medianNs": 202.29527343750055, + "p10Ns": 194.57396484375056, + "p90Ns": 214.54651367187606, + "opsPerSecond": 4943269.227241494 + }, + { + "name": "nullable query", + "iterations": 102400, + "newOutputObjects": 3, + "medianNs": 625.883134765628, + "p10Ns": 605.5560156250017, + "p90Ns": 674.15791015625, + "opsPerSecond": 1597742.3650734192 + }, + { + "name": "connection / 100 rows", + "iterations": 200, + "newOutputObjects": 703, + "medianNs": 267561.3300000009, + "p10Ns": 252490.7950000011, + "p90Ns": 314063.4150000005, + "opsPerSecond": 3737.4608655144475 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 1600, + "newOutputObjects": 301, + "medianNs": 38205.29124999951, + "p10Ns": 36911.118125000256, + "p90Ns": 41716.802500000085, + "opsPerSecond": 26174.38494203373 + }, + { + "name": "union list / 100 rows", + "iterations": 800, + "newOutputObjects": 302, + "medianNs": 77325.44500000018, + "p10Ns": 75914.19500000028, + "p90Ns": 84684.0025000006, + "opsPerSecond": 12932.353638572627 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "medianNs": 10719.061718749997, + "p10Ns": 10580.89640624999, + "p90Ns": 11061.570468749976, + "opsPerSecond": 93291.74756506718 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 2, + "medianNs": 419.1014160156303, + "p10Ns": 397.51949218749695, + "p90Ns": 480.5745214843782, + "opsPerSecond": 2386057.3164055003 + }, + { + "name": "recursive inputs", + "iterations": 400, + "newOutputObjects": 203, + "medianNs": 117512.12249999752, + "p10Ns": 116141.18750000215, + "p90Ns": 126153.6524999974, + "opsPerSecond": 8509.760344087234 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/reverse-first-pass.json b/packages/rescript-relay/benchmarks/results/reverse-first-pass.json new file mode 100644 index 00000000..7aa0ad3d --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/reverse-first-pass.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 2, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "/tmp/relay-conversion-audit/first-pass-utils.cjs", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "medianNs": 253.040908203125, + "p10Ns": 241.3398388671875, + "p90Ns": 281.21173828125, + "opsPerSecond": 3951930.1724812975 + }, + { + "name": "unchanged object", + "iterations": 409600, + "newOutputObjects": 0, + "medianNs": 121.58534667968733, + "p10Ns": 116.83872558593767, + "p90Ns": 143.66232910156205, + "opsPerSecond": 8224675.319094723 + }, + { + "name": "nullable query", + "iterations": 102400, + "newOutputObjects": 3, + "medianNs": 404.78134765625117, + "p10Ns": 396.8749023437512, + "p90Ns": 427.9252929687516, + "opsPerSecond": 2470469.565334866 + }, + { + "name": "connection / 100 rows", + "iterations": 800, + "newOutputObjects": 403, + "medianNs": 63426.453750000175, + "p10Ns": 60388.26125000014, + "p90Ns": 69403.59000000001, + "opsPerSecond": 15766.292152192053 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 1600, + "newOutputObjects": 301, + "medianNs": 32721.201249999864, + "p10Ns": 26765.218125000276, + "p90Ns": 36994.24749999992, + "opsPerSecond": 30561.225193405888 + }, + { + "name": "union list / 100 rows", + "iterations": 800, + "newOutputObjects": 302, + "medianNs": 57567.09124999929, + "p10Ns": 55507.58624999958, + "p90Ns": 62471.36124999997, + "opsPerSecond": 17371.035747789538 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "medianNs": 11124.992968750006, + "p10Ns": 10735.138906249944, + "p90Ns": 12089.70249999993, + "opsPerSecond": 89887.69726048278 + }, + { + "name": "opaque JSON list", + "iterations": 204800, + "newOutputObjects": 0, + "medianNs": 360.07902343750067, + "p10Ns": 341.20398925781444, + "p90Ns": 434.11163574218483, + "opsPerSecond": 2777168.1628479287 + }, + { + "name": "recursive inputs", + "iterations": 1600, + "newOutputObjects": 103, + "medianNs": 29685.76999999982, + "p10Ns": 29033.398749999153, + "p90Ns": 32156.107499999962, + "opsPerSecond": 33686.17354375534 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/reverse-legacy.json b/packages/rescript-relay/benchmarks/results/reverse-legacy.json new file mode 100644 index 00000000..ef520fd5 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/reverse-legacy.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 2, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "medianNs": 260.67492675781233, + "p10Ns": 239.32749023437503, + "p90Ns": 301.7376513671877, + "opsPerSecond": 3836195.572921669 + }, + { + "name": "unchanged object", + "iterations": 409600, + "newOutputObjects": 0, + "medianNs": 112.27575683593726, + "p10Ns": 105.71707031249989, + "p90Ns": 131.49184082031252, + "opsPerSecond": 8906642.254580818 + }, + { + "name": "nullable query", + "iterations": 102400, + "newOutputObjects": 3, + "medianNs": 421.81659179687523, + "p10Ns": 390.69702148437193, + "p90Ns": 462.98953124999986, + "opsPerSecond": 2370698.5913952566 + }, + { + "name": "connection / 100 rows", + "iterations": 800, + "newOutputObjects": 403, + "medianNs": 63811.621250000026, + "p10Ns": 60489.25374999954, + "p90Ns": 72891.10874999949, + "opsPerSecond": 15671.126675848243 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 1600, + "newOutputObjects": 301, + "medianNs": 28267.069999999992, + "p10Ns": 26293.100624999966, + "p90Ns": 34923.04499999989, + "opsPerSecond": 35376.85370291297 + }, + { + "name": "union list / 100 rows", + "iterations": 800, + "newOutputObjects": 302, + "medianNs": 68805.03375000104, + "p10Ns": 60897.09875000039, + "p90Ns": 73596.8562500011, + "opsPerSecond": 14533.82035438628 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "medianNs": 11918.6668750001, + "p10Ns": 11276.09593750009, + "p90Ns": 12962.850000000117, + "opsPerSecond": 83902.00099455264 + }, + { + "name": "opaque JSON list", + "iterations": 204800, + "newOutputObjects": 0, + "medianNs": 297.9844628906259, + "p10Ns": 294.3363525390641, + "p90Ns": 325.21131347655887, + "opsPerSecond": 3355879.6666759313 + }, + { + "name": "recursive inputs", + "iterations": 1600, + "newOutputObjects": 103, + "medianNs": 29947.19562499995, + "p10Ns": 27137.716249999925, + "p90Ns": 34317.94437500002, + "opsPerSecond": 33392.10831364787 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/reverse-master.json b/packages/rescript-relay/benchmarks/results/reverse-master.json new file mode 100644 index 00000000..adfc0356 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/reverse-master.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 2, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "/tmp/rescript-relay-utils-baseline.js", + "results": [ + { + "name": "small fragment", + "iterations": 51200, + "newOutputObjects": 3, + "medianNs": 1390.3375976562502, + "p10Ns": 1310.1150781250005, + "p90Ns": 1465.3082226562497, + "opsPerSecond": 719249.7719156423 + }, + { + "name": "unchanged object", + "iterations": 51200, + "newOutputObjects": 1, + "medianNs": 814.9233984374993, + "p10Ns": 743.1374999999996, + "p90Ns": 938.5449609374996, + "opsPerSecond": 1227109.2005915637 + }, + { + "name": "nullable query", + "iterations": 25600, + "newOutputObjects": 3, + "medianNs": 2151.2362109374903, + "p10Ns": 2004.3573828125006, + "p90Ns": 2345.9887109375013, + "opsPerSecond": 464848.9993408063 + }, + { + "name": "connection / 100 rows", + "iterations": 100, + "newOutputObjects": 703, + "medianNs": 730193.2099999976, + "p10Ns": 713429.6300000006, + "p90Ns": 806732.8899999984, + "opsPerSecond": 1369.5005463006198 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 400, + "newOutputObjects": 301, + "medianNs": 144272.67999999912, + "p10Ns": 137757.06250000212, + "p90Ns": 162907.15249999947, + "opsPerSecond": 6931.319221352276 + }, + { + "name": "union list / 100 rows", + "iterations": 200, + "newOutputObjects": 302, + "medianNs": 231484.95499999626, + "p10Ns": 219309.6400000013, + "p90Ns": 252264.18000000194, + "opsPerSecond": 4319.935176780781 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "medianNs": 11409.869999999955, + "p10Ns": 11052.804374999992, + "p90Ns": 12036.685937500237, + "opsPerSecond": 87643.4174973075 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 2, + "medianNs": 561.9654492187465, + "p10Ns": 547.2058105468758, + "p90Ns": 620.7305273437491, + "opsPerSecond": 1779468.8292495853 + }, + { + "name": "recursive inputs", + "iterations": 200, + "newOutputObjects": 203, + "medianNs": 330408.38999999774, + "p10Ns": 316034.76499999483, + "p90Ns": 367772.06500000827, + "opsPerSecond": 3026.5575277916123 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/reverse-pr-673.json b/packages/rescript-relay/benchmarks/results/reverse-pr-673.json new file mode 100644 index 00000000..e3aec883 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/reverse-pr-673.json @@ -0,0 +1,93 @@ +{ + "fixtureVersion": 2, + "mode": "legacy", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "/tmp/relay-conversion-audit/pr-673-utils.cjs", + "results": [ + { + "name": "small fragment", + "iterations": 102400, + "newOutputObjects": 3, + "medianNs": 395.65080078124976, + "p10Ns": 370.56431640625, + "p90Ns": 475.68430664062464, + "opsPerSecond": 2527481.299230043 + }, + { + "name": "unchanged object", + "iterations": 204800, + "newOutputObjects": 1, + "medianNs": 216.4925732421874, + "p10Ns": 200.19873535156216, + "p90Ns": 236.6231005859376, + "opsPerSecond": 4619096.096572852 + }, + { + "name": "nullable query", + "iterations": 102400, + "newOutputObjects": 3, + "medianNs": 687.3554101562496, + "p10Ns": 636.9974316406247, + "p90Ns": 787.6288378906261, + "opsPerSecond": 1454851.4279864037 + }, + { + "name": "connection / 100 rows", + "iterations": 200, + "newOutputObjects": 703, + "medianNs": 284588.53999999975, + "p10Ns": 251499.08999999982, + "p90Ns": 304142.2499999999, + "opsPerSecond": 3513.844935569088 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 1600, + "newOutputObjects": 301, + "medianNs": 43816.24812499979, + "p10Ns": 37508.75624999992, + "p90Ns": 54928.126874999914, + "opsPerSecond": 22822.583922456844 + }, + { + "name": "union list / 100 rows", + "iterations": 800, + "newOutputObjects": 302, + "medianNs": 84566.53749999987, + "p10Ns": 79368.84999999961, + "p90Ns": 101158.51249999991, + "opsPerSecond": 11825.008207294777 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "medianNs": 11367.214687499825, + "p10Ns": 10906.34343749997, + "p90Ns": 12333.685312499938, + "opsPerSecond": 87972.29818309573 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 2, + "medianNs": 678.9901757812445, + "p10Ns": 657.6561035156292, + "p90Ns": 765.7203320312522, + "opsPerSecond": 1472775.3591565627 + }, + { + "name": "recursive inputs", + "iterations": 400, + "newOutputObjects": 203, + "medianNs": 130241.60999999821, + "p10Ns": 115395.29749999929, + "p90Ns": 147263.88499999756, + "opsPerSecond": 7678.037763814604 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/reverse-prepared.json b/packages/rescript-relay/benchmarks/results/reverse-prepared.json new file mode 100644 index 00000000..da775e64 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/reverse-prepared.json @@ -0,0 +1,102 @@ +{ + "fixtureVersion": 2, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 4430.986000000001, + "medianNs": 120.84148681640622, + "p10Ns": 115.67026367187496, + "p90Ns": 147.16306884765626, + "opsPerSecond": 8275303.67546118 + }, + { + "name": "unchanged object", + "iterations": 819200, + "newOutputObjects": 0, + "preparationNs": 1428.4999999999857, + "medianNs": 61.56754028320333, + "p10Ns": 56.9567492675782, + "p90Ns": 76.20194213867171, + "opsPerSecond": 16242325.020621572 + }, + { + "name": "nullable query", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 933.4850000000188, + "medianNs": 152.60232910156267, + "p10Ns": 143.74919189453104, + "p90Ns": 165.92133056640648, + "opsPerSecond": 6552979.930827018 + }, + { + "name": "connection / 100 rows", + "iterations": 3200, + "newOutputObjects": 403, + "preparationNs": 3173.428999999942, + "medianNs": 20724.01187500006, + "p10Ns": 20384.20375000001, + "p90Ns": 23311.01781249998, + "opsPerSecond": 48253.20531717738 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 3200, + "newOutputObjects": 301, + "preparationNs": 1140.4360000001361, + "medianNs": 15191.98593750019, + "p10Ns": 14558.485312499839, + "p90Ns": 16636.810937500002, + "opsPerSecond": 65824.17888707893 + }, + { + "name": "union list / 100 rows", + "iterations": 3200, + "newOutputObjects": 302, + "preparationNs": 3697.0489999994243, + "medianNs": 18346.74906250001, + "p10Ns": 17551.937499999894, + "p90Ns": 22648.20531250024, + "opsPerSecond": 54505.56916614499 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "preparationNs": 1487.8020000005563, + "medianNs": 12115.780312499994, + "p10Ns": 11397.223593749997, + "p90Ns": 12894.748125000035, + "opsPerSecond": 82536.98682273796 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 0, + "preparationNs": 1891.5559999995821, + "medianNs": 460.2599609374991, + "p10Ns": 447.00962890624754, + "p90Ns": 477.52247070312666, + "opsPerSecond": 2172685.1885250015 + }, + { + "name": "recursive inputs", + "iterations": 3200, + "newOutputObjects": 103, + "preparationNs": 3538.0679999998392, + "medianNs": 16372.539062499527, + "p10Ns": 16073.624062499903, + "p90Ns": 17017.99968749981, + "opsPerSecond": 61077.88145642293 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-before.json b/packages/rescript-relay/benchmarks/results/size-before.json new file mode 100644 index 00000000..be4c2af9 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-before.json @@ -0,0 +1,792 @@ +{ + "fixtureVersion": 1, + "esbuild": "0.25.12", + "node": "v24.16.0", + "artifactCount": 129, + "combined": { + "minified": 313772, + "gzip": 41628, + "brotli": 29248 + }, + "separate": { + "minified": 342285, + "gzip": 126052, + "brotli": 112258 + }, + "files": [ + { + "path": "__tests__/__generated__/GroupAvatar_group_graphql.bs.js", + "minified": 848, + "gzip": 498, + "brotli": 428 + }, + { + "path": "__tests__/__generated__/HasNameComponent_hasName_graphql.bs.js", + "minified": 942, + "gzip": 517, + "brotli": 448 + }, + { + "path": "__tests__/__generated__/LocalUser____relay_model_instance_graphql.bs.js", + "minified": 1196, + "gzip": 632, + "brotli": 555 + }, + { + "path": "__tests__/__generated__/LocalUser__id_graphql.bs.js", + "minified": 884, + "gzip": 512, + "brotli": 440 + }, + { + "path": "__tests__/__generated__/LocalUser_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/Query_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/RichContent_content_graphql.bs.js", + "minified": 859, + "gzip": 497, + "brotli": 427 + }, + { + "path": "__tests__/__generated__/TestAliasedFragmentsQuery_graphql.bs.js", + "minified": 3560, + "gzip": 1233, + "brotli": 1100 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.bs.js", + "minified": 869, + "gzip": 507, + "brotli": 438 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userLastName_graphql.bs.js", + "minified": 867, + "gzip": 507, + "brotli": 436 + }, + { + "path": "__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.bs.js", + "minified": 3312, + "gzip": 1284, + "brotli": 1143 + }, + { + "path": "__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.bs.js", + "minified": 3204, + "gzip": 1231, + "brotli": 1093 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.bs.js", + "minified": 4680, + "gzip": 1463, + "brotli": 1332 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropQuery_graphql.bs.js", + "minified": 3587, + "gzip": 1356, + "brotli": 1222 + }, + { + "path": "__tests__/__generated__/TestCatchMembersPropQuery_graphql.bs.js", + "minified": 4389, + "gzip": 1450, + "brotli": 1309 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMemberQuery_graphql.bs.js", + "minified": 3429, + "gzip": 1272, + "brotli": 1132 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMember_member_graphql.bs.js", + "minified": 1599, + "gzip": 718, + "brotli": 646 + }, + { + "path": "__tests__/__generated__/TestCatchUser_user_graphql.bs.js", + "minified": 984, + "gzip": 565, + "brotli": 493 + }, + { + "path": "__tests__/__generated__/TestCodesplitQuery_graphql.bs.js", + "minified": 7429, + "gzip": 1886, + "brotli": 1711 + }, + { + "path": "__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.bs.js", + "minified": 868, + "gzip": 510, + "brotli": 439 + }, + { + "path": "__tests__/__generated__/TestConnections2_user_graphql.bs.js", + "minified": 4166, + "gzip": 1362, + "brotli": 1242 + }, + { + "path": "__tests__/__generated__/TestConnections3_user_graphql.bs.js", + "minified": 2410, + "gzip": 878, + "brotli": 787 + }, + { + "path": "__tests__/__generated__/TestConnectionsQuery_graphql.bs.js", + "minified": 4527, + "gzip": 1573, + "brotli": 1409 + }, + { + "path": "__tests__/__generated__/TestConnections_AddFriendMutation_graphql.bs.js", + "minified": 2625, + "gzip": 972, + "brotli": 866 + }, + { + "path": "__tests__/__generated__/TestConnections_user_graphql.bs.js", + "minified": 3075, + "gzip": 1092, + "brotli": 983 + }, + { + "path": "__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 3189, + "gzip": 1184, + "brotli": 1060 + }, + { + "path": "__tests__/__generated__/TestCustomScalarsQuery_graphql.bs.js", + "minified": 4938, + "gzip": 1555, + "brotli": 1398 + }, + { + "path": "__tests__/__generated__/TestFragmentQuery_graphql.bs.js", + "minified": 4740, + "gzip": 1424, + "brotli": 1281 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.bs.js", + "minified": 1040, + "gzip": 588, + "brotli": 515 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredQuery_graphql.bs.js", + "minified": 3906, + "gzip": 1237, + "brotli": 1107 + }, + { + "path": "__tests__/__generated__/TestFragmentRequired_user_graphql.bs.js", + "minified": 1027, + "gzip": 577, + "brotli": 504 + }, + { + "path": "__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.bs.js", + "minified": 1062, + "gzip": 575, + "brotli": 498 + }, + { + "path": "__tests__/__generated__/TestFragment_inline_graphql.bs.js", + "minified": 831, + "gzip": 481, + "brotli": 420 + }, + { + "path": "__tests__/__generated__/TestFragment_plural_user_graphql.bs.js", + "minified": 1133, + "gzip": 581, + "brotli": 507 + }, + { + "path": "__tests__/__generated__/TestFragment_sub_user_graphql.bs.js", + "minified": 855, + "gzip": 503, + "brotli": 433 + }, + { + "path": "__tests__/__generated__/TestFragment_user_graphql.bs.js", + "minified": 1244, + "gzip": 620, + "brotli": 535 + }, + { + "path": "__tests__/__generated__/TestInputUnionQuery_graphql.bs.js", + "minified": 2536, + "gzip": 1104, + "brotli": 971 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadQuery_graphql.bs.js", + "minified": 6273, + "gzip": 1673, + "brotli": 1505 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.bs.js", + "minified": 7017, + "gzip": 1812, + "brotli": 1638 + }, + { + "path": "__tests__/__generated__/TestLocalPayload_user_graphql.bs.js", + "minified": 2623, + "gzip": 870, + "brotli": 776 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.bs.js", + "minified": 2609, + "gzip": 1067, + "brotli": 946 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.bs.js", + "minified": 3101, + "gzip": 1234, + "brotli": 1107 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.bs.js", + "minified": 2694, + "gzip": 1088, + "brotli": 966 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.bs.js", + "minified": 2686, + "gzip": 1088, + "brotli": 971 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.bs.js", + "minified": 2607, + "gzip": 1063, + "brotli": 958 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.bs.js", + "minified": 3097, + "gzip": 1232, + "brotli": 1097 + }, + { + "path": "__tests__/__generated__/TestMutationInline_user_graphql.bs.js", + "minified": 835, + "gzip": 488, + "brotli": 426 + }, + { + "path": "__tests__/__generated__/TestMutationProvidedVariable_user_graphql.bs.js", + "minified": 1035, + "gzip": 563, + "brotli": 486 + }, + { + "path": "__tests__/__generated__/TestMutationQuery_graphql.bs.js", + "minified": 3563, + "gzip": 1263, + "brotli": 1125 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.bs.js", + "minified": 2812, + "gzip": 1058, + "brotli": 967 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.bs.js", + "minified": 3688, + "gzip": 1121, + "brotli": 1011 + }, + { + "path": "__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 3623, + "gzip": 1101, + "brotli": 981 + }, + { + "path": "__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 4721, + "gzip": 1329, + "brotli": 1205 + }, + { + "path": "__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.bs.js", + "minified": 3171, + "gzip": 1023, + "brotli": 912 + }, + { + "path": "__tests__/__generated__/TestMutation_user_graphql.bs.js", + "minified": 1860, + "gzip": 753, + "brotli": 664 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.bs.js", + "minified": 3530, + "gzip": 1301, + "brotli": 1167 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceQuery_graphql.bs.js", + "minified": 4323, + "gzip": 1391, + "brotli": 1260 + }, + { + "path": "__tests__/__generated__/TestNodeInterface_user_graphql.bs.js", + "minified": 857, + "gzip": 506, + "brotli": 434 + }, + { + "path": "__tests__/__generated__/TestNonReactQuery_graphql.bs.js", + "minified": 2830, + "gzip": 1130, + "brotli": 1000 + }, + { + "path": "__tests__/__generated__/TestNonReact_user_graphql.bs.js", + "minified": 1051, + "gzip": 570, + "brotli": 494 + }, + { + "path": "__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 3248, + "gzip": 1233, + "brotli": 1107 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesMutation_graphql.bs.js", + "minified": 3119, + "gzip": 1080, + "brotli": 988 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesQuery_graphql.bs.js", + "minified": 2565, + "gzip": 1053, + "brotli": 938 + }, + { + "path": "__tests__/__generated__/TestObserverQuery_graphql.bs.js", + "minified": 2317, + "gzip": 1025, + "brotli": 907 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeQuery_graphql.bs.js", + "minified": 4725, + "gzip": 1511, + "brotli": 1361 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.bs.js", + "minified": 5376, + "gzip": 1690, + "brotli": 1522 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_query_graphql.bs.js", + "minified": 2832, + "gzip": 1072, + "brotli": 961 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_user_graphql.bs.js", + "minified": 1199, + "gzip": 596, + "brotli": 517 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionQuery_graphql.bs.js", + "minified": 5288, + "gzip": 1614, + "brotli": 1451 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.bs.js", + "minified": 6005, + "gzip": 1785, + "brotli": 1604 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_query_graphql.bs.js", + "minified": 3940, + "gzip": 1269, + "brotli": 1141 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_user_graphql.bs.js", + "minified": 1130, + "gzip": 590, + "brotli": 509 + }, + { + "path": "__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.bs.js", + "minified": 3347, + "gzip": 1181, + "brotli": 1052 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.bs.js", + "minified": 4019, + "gzip": 1348, + "brotli": 1212 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.bs.js", + "minified": 4569, + "gzip": 1494, + "brotli": 1336 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.bs.js", + "minified": 1243, + "gzip": 603, + "brotli": 536 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user_graphql.bs.js", + "minified": 2284, + "gzip": 896, + "brotli": 794 + }, + { + "path": "__tests__/__generated__/TestProvidedVariablesQuery_graphql.bs.js", + "minified": 6031, + "gzip": 1604, + "brotli": 1451 + }, + { + "path": "__tests__/__generated__/TestProvidedVariables_user_graphql.bs.js", + "minified": 1718, + "gzip": 620, + "brotli": 545 + }, + { + "path": "__tests__/__generated__/TestQueryInputObjectsQuery_graphql.bs.js", + "minified": 2457, + "gzip": 1095, + "brotli": 963 + }, + { + "path": "__tests__/__generated__/TestQuery_graphql.bs.js", + "minified": 3099, + "gzip": 1231, + "brotli": 1104 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeQuery_graphql.bs.js", + "minified": 4181, + "gzip": 1484, + "brotli": 1347 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.bs.js", + "minified": 4215, + "gzip": 1477, + "brotli": 1328 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNode_user_graphql.bs.js", + "minified": 1842, + "gzip": 820, + "brotli": 721 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.bs.js", + "minified": 2541, + "gzip": 1071, + "brotli": 954 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgs_query_graphql.bs.js", + "minified": 1125, + "gzip": 611, + "brotli": 534 + }, + { + "path": "__tests__/__generated__/TestRefetchingQuery_graphql.bs.js", + "minified": 4097, + "gzip": 1452, + "brotli": 1300 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.bs.js", + "minified": 2583, + "gzip": 1078, + "brotli": 956 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.bs.js", + "minified": 1137, + "gzip": 616, + "brotli": 537 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordQuery_graphql.bs.js", + "minified": 4139, + "gzip": 1458, + "brotli": 1304 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.bs.js", + "minified": 4924, + "gzip": 1658, + "brotli": 1506 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecord_user_graphql.bs.js", + "minified": 2242, + "gzip": 895, + "brotli": 788 + }, + { + "path": "__tests__/__generated__/TestRefetchingRefetchQuery_graphql.bs.js", + "minified": 4882, + "gzip": 1656, + "brotli": 1496 + }, + { + "path": "__tests__/__generated__/TestRefetching_user_graphql.bs.js", + "minified": 2230, + "gzip": 891, + "brotli": 784 + }, + { + "path": "__tests__/__generated__/TestRelayResolversAllQuery_graphql.bs.js", + "minified": 5785, + "gzip": 1641, + "brotli": 1487 + }, + { + "path": "__tests__/__generated__/TestRelayResolversQuery_graphql.bs.js", + "minified": 3408, + "gzip": 1236, + "brotli": 1103 + }, + { + "path": "__tests__/__generated__/TestRelayResolvers_user_graphql.bs.js", + "minified": 1437, + "gzip": 678, + "brotli": 595 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver2_graphql.bs.js", + "minified": 931, + "gzip": 513, + "brotli": 443 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver_graphql.bs.js", + "minified": 930, + "gzip": 511, + "brotli": 442 + }, + { + "path": "__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.bs.js", + "minified": 2615, + "gzip": 1072, + "brotli": 960 + }, + { + "path": "__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.bs.js", + "minified": 2204, + "gzip": 911, + "brotli": 808 + }, + { + "path": "__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.bs.js", + "minified": 2293, + "gzip": 902, + "brotli": 786 + }, + { + "path": "__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.bs.js", + "minified": 1039, + "gzip": 565, + "brotli": 485 + }, + { + "path": "__tests__/__generated__/TestSubscriptionQuery_graphql.bs.js", + "minified": 2904, + "gzip": 1128, + "brotli": 1000 + }, + { + "path": "__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.bs.js", + "minified": 2768, + "gzip": 980, + "brotli": 889 + }, + { + "path": "__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.bs.js", + "minified": 3254, + "gzip": 1099, + "brotli": 986 + }, + { + "path": "__tests__/__generated__/TestSubscription_user_graphql.bs.js", + "minified": 1198, + "gzip": 585, + "brotli": 504 + }, + { + "path": "__tests__/__generated__/TestTestingHelpersQuery_graphql.bs.js", + "minified": 4122, + "gzip": 1400, + "brotli": 1253 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_plural_user_graphql.bs.js", + "minified": 1139, + "gzip": 589, + "brotli": 514 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_sub_user_graphql.bs.js", + "minified": 861, + "gzip": 509, + "brotli": 441 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_user_graphql.bs.js", + "minified": 1256, + "gzip": 627, + "brotli": 543 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentQuery_graphql.bs.js", + "minified": 3912, + "gzip": 1318, + "brotli": 1165 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentUser_user_graphql.bs.js", + "minified": 1060, + "gzip": 573, + "brotli": 494 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_member_graphql.bs.js", + "minified": 1632, + "gzip": 716, + "brotli": 628 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_plural_member_graphql.bs.js", + "minified": 1646, + "gzip": 727, + "brotli": 643 + }, + { + "path": "__tests__/__generated__/TestUnionsQuery_graphql.bs.js", + "minified": 5767, + "gzip": 1592, + "brotli": 1443 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.bs.js", + "minified": 3164, + "gzip": 1174, + "brotli": 1065 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_query_graphql.bs.js", + "minified": 1324, + "gzip": 625, + "brotli": 536 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_updatableUser_graphql.bs.js", + "minified": 678, + "gzip": 363, + "brotli": 321 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.bs.js", + "minified": 1428, + "gzip": 658, + "brotli": 574 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_graphql.bs.js", + "minified": 3031, + "gzip": 1148, + "brotli": 1028 + }, + { + "path": "__tests__/__generated__/UserAvatar_user_graphql.bs.js", + "minified": 1186, + "gzip": 621, + "brotli": 541 + }, + { + "path": "__tests__/__generated__/UserMeta____relay_model_instance_graphql.bs.js", + "minified": 922, + "gzip": 524, + "brotli": 450 + }, + { + "path": "__tests__/__generated__/UserMeta_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/UserName_user_graphql.bs.js", + "minified": 922, + "gzip": 507, + "brotli": 437 + }, + { + "path": "__tests__/__generated__/User_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.bs.js", + "minified": 1555, + "gzip": 619, + "brotli": 541 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.bs.js", + "minified": 5480, + "gzip": 1565, + "brotli": 1404 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.bs.js", + "minified": 2369, + "gzip": 871, + "brotli": 787 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-callback-slots.json b/packages/rescript-relay/benchmarks/results/size-callback-slots.json new file mode 100644 index 00000000..c61420d6 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-callback-slots.json @@ -0,0 +1,792 @@ +{ + "fixtureVersion": 1, + "esbuild": "0.25.12", + "node": "v24.16.0", + "artifactCount": 129, + "combined": { + "minified": 274404, + "gzip": 36667, + "brotli": 26495 + }, + "separate": { + "minified": 304216, + "gzip": 118974, + "brotli": 105519 + }, + "files": [ + { + "path": "__tests__/__generated__/GroupAvatar_group_graphql.bs.js", + "minified": 711, + "gzip": 447, + "brotli": 384 + }, + { + "path": "__tests__/__generated__/HasNameComponent_hasName_graphql.bs.js", + "minified": 805, + "gzip": 467, + "brotli": 400 + }, + { + "path": "__tests__/__generated__/LocalUser____relay_model_instance_graphql.bs.js", + "minified": 1059, + "gzip": 579, + "brotli": 506 + }, + { + "path": "__tests__/__generated__/LocalUser__id_graphql.bs.js", + "minified": 747, + "gzip": 462, + "brotli": 399 + }, + { + "path": "__tests__/__generated__/LocalUser_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/Query_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/RichContent_content_graphql.bs.js", + "minified": 722, + "gzip": 447, + "brotli": 380 + }, + { + "path": "__tests__/__generated__/TestAliasedFragmentsQuery_graphql.bs.js", + "minified": 3136, + "gzip": 1125, + "brotli": 1003 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.bs.js", + "minified": 732, + "gzip": 455, + "brotli": 394 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userLastName_graphql.bs.js", + "minified": 730, + "gzip": 454, + "brotli": 391 + }, + { + "path": "__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.bs.js", + "minified": 2930, + "gzip": 1217, + "brotli": 1073 + }, + { + "path": "__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.bs.js", + "minified": 2721, + "gzip": 1165, + "brotli": 1035 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.bs.js", + "minified": 3889, + "gzip": 1386, + "brotli": 1260 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropQuery_graphql.bs.js", + "minified": 3020, + "gzip": 1285, + "brotli": 1159 + }, + { + "path": "__tests__/__generated__/TestCatchMembersPropQuery_graphql.bs.js", + "minified": 3708, + "gzip": 1381, + "brotli": 1244 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMemberQuery_graphql.bs.js", + "minified": 3073, + "gzip": 1217, + "brotli": 1078 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMember_member_graphql.bs.js", + "minified": 1472, + "gzip": 691, + "brotli": 604 + }, + { + "path": "__tests__/__generated__/TestCatchUser_user_graphql.bs.js", + "minified": 875, + "gzip": 542, + "brotli": 465 + }, + { + "path": "__tests__/__generated__/TestCodesplitQuery_graphql.bs.js", + "minified": 7005, + "gzip": 1782, + "brotli": 1615 + }, + { + "path": "__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.bs.js", + "minified": 731, + "gzip": 458, + "brotli": 393 + }, + { + "path": "__tests__/__generated__/TestConnections2_user_graphql.bs.js", + "minified": 4067, + "gzip": 1345, + "brotli": 1220 + }, + { + "path": "__tests__/__generated__/TestConnections3_user_graphql.bs.js", + "minified": 2334, + "gzip": 860, + "brotli": 766 + }, + { + "path": "__tests__/__generated__/TestConnectionsQuery_graphql.bs.js", + "minified": 4183, + "gzip": 1508, + "brotli": 1348 + }, + { + "path": "__tests__/__generated__/TestConnections_AddFriendMutation_graphql.bs.js", + "minified": 2252, + "gzip": 914, + "brotli": 809 + }, + { + "path": "__tests__/__generated__/TestConnections_user_graphql.bs.js", + "minified": 2999, + "gzip": 1075, + "brotli": 966 + }, + { + "path": "__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2616, + "gzip": 1119, + "brotli": 992 + }, + { + "path": "__tests__/__generated__/TestCustomScalarsQuery_graphql.bs.js", + "minified": 4182, + "gzip": 1475, + "brotli": 1336 + }, + { + "path": "__tests__/__generated__/TestFragmentQuery_graphql.bs.js", + "minified": 4301, + "gzip": 1367, + "brotli": 1223 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.bs.js", + "minified": 903, + "gzip": 534, + "brotli": 463 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredQuery_graphql.bs.js", + "minified": 3467, + "gzip": 1182, + "brotli": 1050 + }, + { + "path": "__tests__/__generated__/TestFragmentRequired_user_graphql.bs.js", + "minified": 890, + "gzip": 525, + "brotli": 452 + }, + { + "path": "__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.bs.js", + "minified": 925, + "gzip": 524, + "brotli": 451 + }, + { + "path": "__tests__/__generated__/TestFragment_inline_graphql.bs.js", + "minified": 694, + "gzip": 430, + "brotli": 372 + }, + { + "path": "__tests__/__generated__/TestFragment_plural_user_graphql.bs.js", + "minified": 996, + "gzip": 531, + "brotli": 458 + }, + { + "path": "__tests__/__generated__/TestFragment_sub_user_graphql.bs.js", + "minified": 718, + "gzip": 450, + "brotli": 387 + }, + { + "path": "__tests__/__generated__/TestFragment_user_graphql.bs.js", + "minified": 1168, + "gzip": 600, + "brotli": 513 + }, + { + "path": "__tests__/__generated__/TestInputUnionQuery_graphql.bs.js", + "minified": 2163, + "gzip": 1037, + "brotli": 904 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadQuery_graphql.bs.js", + "minified": 5054, + "gzip": 1560, + "brotli": 1408 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.bs.js", + "minified": 5686, + "gzip": 1693, + "brotli": 1525 + }, + { + "path": "__tests__/__generated__/TestLocalPayload_user_graphql.bs.js", + "minified": 2408, + "gzip": 847, + "brotli": 753 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.bs.js", + "minified": 2185, + "gzip": 961, + "brotli": 844 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.bs.js", + "minified": 2708, + "gzip": 1169, + "brotli": 1043 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.bs.js", + "minified": 2337, + "gzip": 1033, + "brotli": 913 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.bs.js", + "minified": 2325, + "gzip": 1033, + "brotli": 911 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.bs.js", + "minified": 2183, + "gzip": 956, + "brotli": 844 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.bs.js", + "minified": 2704, + "gzip": 1167, + "brotli": 1040 + }, + { + "path": "__tests__/__generated__/TestMutationInline_user_graphql.bs.js", + "minified": 698, + "gzip": 434, + "brotli": 376 + }, + { + "path": "__tests__/__generated__/TestMutationProvidedVariable_user_graphql.bs.js", + "minified": 898, + "gzip": 514, + "brotli": 443 + }, + { + "path": "__tests__/__generated__/TestMutationQuery_graphql.bs.js", + "minified": 3201, + "gzip": 1208, + "brotli": 1066 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.bs.js", + "minified": 2404, + "gzip": 990, + "brotli": 882 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.bs.js", + "minified": 2994, + "gzip": 1025, + "brotli": 923 + }, + { + "path": "__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 2929, + "gzip": 1004, + "brotli": 900 + }, + { + "path": "__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 3910, + "gzip": 1231, + "brotli": 1116 + }, + { + "path": "__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.bs.js", + "minified": 2788, + "gzip": 967, + "brotli": 880 + }, + { + "path": "__tests__/__generated__/TestMutation_user_graphql.bs.js", + "minified": 1757, + "gzip": 732, + "brotli": 645 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.bs.js", + "minified": 3137, + "gzip": 1238, + "brotli": 1103 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceQuery_graphql.bs.js", + "minified": 3720, + "gzip": 1318, + "brotli": 1193 + }, + { + "path": "__tests__/__generated__/TestNodeInterface_user_graphql.bs.js", + "minified": 720, + "gzip": 454, + "brotli": 387 + }, + { + "path": "__tests__/__generated__/TestNonReactQuery_graphql.bs.js", + "minified": 2468, + "gzip": 1077, + "brotli": 944 + }, + { + "path": "__tests__/__generated__/TestNonReact_user_graphql.bs.js", + "minified": 914, + "gzip": 519, + "brotli": 447 + }, + { + "path": "__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2798, + "gzip": 1170, + "brotli": 1035 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesMutation_graphql.bs.js", + "minified": 2713, + "gzip": 1016, + "brotli": 908 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesQuery_graphql.bs.js", + "minified": 2141, + "gzip": 948, + "brotli": 834 + }, + { + "path": "__tests__/__generated__/TestObserverQuery_graphql.bs.js", + "minified": 1893, + "gzip": 922, + "brotli": 813 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeQuery_graphql.bs.js", + "minified": 4371, + "gzip": 1458, + "brotli": 1305 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.bs.js", + "minified": 5073, + "gzip": 1630, + "brotli": 1473 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_query_graphql.bs.js", + "minified": 2756, + "gzip": 1055, + "brotli": 940 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_user_graphql.bs.js", + "minified": 1062, + "gzip": 549, + "brotli": 475 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionQuery_graphql.bs.js", + "minified": 4940, + "gzip": 1564, + "brotli": 1406 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.bs.js", + "minified": 5708, + "gzip": 1728, + "brotli": 1559 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_query_graphql.bs.js", + "minified": 3817, + "gzip": 1250, + "brotli": 1122 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_user_graphql.bs.js", + "minified": 993, + "gzip": 541, + "brotli": 471 + }, + { + "path": "__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.bs.js", + "minified": 2767, + "gzip": 1117, + "brotli": 994 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.bs.js", + "minified": 3657, + "gzip": 1295, + "brotli": 1149 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.bs.js", + "minified": 4215, + "gzip": 1439, + "brotli": 1293 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.bs.js", + "minified": 1106, + "gzip": 555, + "brotli": 486 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user_graphql.bs.js", + "minified": 2208, + "gzip": 879, + "brotli": 776 + }, + { + "path": "__tests__/__generated__/TestProvidedVariablesQuery_graphql.bs.js", + "minified": 5647, + "gzip": 1538, + "brotli": 1388 + }, + { + "path": "__tests__/__generated__/TestProvidedVariables_user_graphql.bs.js", + "minified": 1581, + "gzip": 572, + "brotli": 504 + }, + { + "path": "__tests__/__generated__/TestQueryInputObjectsQuery_graphql.bs.js", + "minified": 2084, + "gzip": 1030, + "brotli": 911 + }, + { + "path": "__tests__/__generated__/TestQuery_graphql.bs.js", + "minified": 2742, + "gzip": 1179, + "brotli": 1068 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeQuery_graphql.bs.js", + "minified": 3807, + "gzip": 1422, + "brotli": 1280 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.bs.js", + "minified": 3912, + "gzip": 1420, + "brotli": 1275 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNode_user_graphql.bs.js", + "minified": 1705, + "gzip": 769, + "brotli": 668 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.bs.js", + "minified": 2193, + "gzip": 1016, + "brotli": 902 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgs_query_graphql.bs.js", + "minified": 988, + "gzip": 558, + "brotli": 485 + }, + { + "path": "__tests__/__generated__/TestRefetchingQuery_graphql.bs.js", + "minified": 3719, + "gzip": 1387, + "brotli": 1234 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.bs.js", + "minified": 2235, + "gzip": 1022, + "brotli": 905 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.bs.js", + "minified": 1000, + "gzip": 561, + "brotli": 489 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordQuery_graphql.bs.js", + "minified": 3761, + "gzip": 1393, + "brotli": 1240 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.bs.js", + "minified": 4554, + "gzip": 1593, + "brotli": 1439 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecord_user_graphql.bs.js", + "minified": 2166, + "gzip": 874, + "brotli": 768 + }, + { + "path": "__tests__/__generated__/TestRefetchingRefetchQuery_graphql.bs.js", + "minified": 4512, + "gzip": 1589, + "brotli": 1434 + }, + { + "path": "__tests__/__generated__/TestRefetching_user_graphql.bs.js", + "minified": 2154, + "gzip": 870, + "brotli": 765 + }, + { + "path": "__tests__/__generated__/TestRelayResolversAllQuery_graphql.bs.js", + "minified": 5361, + "gzip": 1536, + "brotli": 1386 + }, + { + "path": "__tests__/__generated__/TestRelayResolversQuery_graphql.bs.js", + "minified": 3046, + "gzip": 1181, + "brotli": 1052 + }, + { + "path": "__tests__/__generated__/TestRelayResolvers_user_graphql.bs.js", + "minified": 1300, + "gzip": 625, + "brotli": 542 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver2_graphql.bs.js", + "minified": 794, + "gzip": 465, + "brotli": 395 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver_graphql.bs.js", + "minified": 793, + "gzip": 463, + "brotli": 395 + }, + { + "path": "__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.bs.js", + "minified": 2191, + "gzip": 967, + "brotli": 855 + }, + { + "path": "__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.bs.js", + "minified": 1796, + "gzip": 841, + "brotli": 735 + }, + { + "path": "__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.bs.js", + "minified": 1829, + "gzip": 827, + "brotli": 719 + }, + { + "path": "__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.bs.js", + "minified": 902, + "gzip": 515, + "brotli": 443 + }, + { + "path": "__tests__/__generated__/TestSubscriptionQuery_graphql.bs.js", + "minified": 2542, + "gzip": 1073, + "brotli": 942 + }, + { + "path": "__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.bs.js", + "minified": 2552, + "gzip": 952, + "brotli": 837 + }, + { + "path": "__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.bs.js", + "minified": 3038, + "gzip": 1066, + "brotli": 953 + }, + { + "path": "__tests__/__generated__/TestSubscription_user_graphql.bs.js", + "minified": 1061, + "gzip": 535, + "brotli": 455 + }, + { + "path": "__tests__/__generated__/TestTestingHelpersQuery_graphql.bs.js", + "minified": 3448, + "gzip": 1298, + "brotli": 1170 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_plural_user_graphql.bs.js", + "minified": 1002, + "gzip": 538, + "brotli": 466 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_sub_user_graphql.bs.js", + "minified": 724, + "gzip": 458, + "brotli": 390 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_user_graphql.bs.js", + "minified": 1180, + "gzip": 607, + "brotli": 521 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentQuery_graphql.bs.js", + "minified": 3556, + "gzip": 1264, + "brotli": 1124 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentUser_user_graphql.bs.js", + "minified": 923, + "gzip": 520, + "brotli": 448 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_member_graphql.bs.js", + "minified": 1547, + "gzip": 696, + "brotli": 609 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_plural_member_graphql.bs.js", + "minified": 1561, + "gzip": 707, + "brotli": 634 + }, + { + "path": "__tests__/__generated__/TestUnionsQuery_graphql.bs.js", + "minified": 5037, + "gzip": 1522, + "brotli": 1380 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.bs.js", + "minified": 2816, + "gzip": 1121, + "brotli": 997 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_query_graphql.bs.js", + "minified": 1248, + "gzip": 607, + "brotli": 517 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_updatableUser_graphql.bs.js", + "minified": 678, + "gzip": 363, + "brotli": 321 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.bs.js", + "minified": 1298, + "gzip": 610, + "brotli": 529 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_graphql.bs.js", + "minified": 2607, + "gzip": 1046, + "brotli": 936 + }, + { + "path": "__tests__/__generated__/UserAvatar_user_graphql.bs.js", + "minified": 1049, + "gzip": 572, + "brotli": 494 + }, + { + "path": "__tests__/__generated__/UserMeta____relay_model_instance_graphql.bs.js", + "minified": 785, + "gzip": 476, + "brotli": 404 + }, + { + "path": "__tests__/__generated__/UserMeta_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/UserName_user_graphql.bs.js", + "minified": 785, + "gzip": 458, + "brotli": 393 + }, + { + "path": "__tests__/__generated__/User_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.bs.js", + "minified": 1418, + "gzip": 570, + "brotli": 499 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.bs.js", + "minified": 5083, + "gzip": 1495, + "brotli": 1342 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.bs.js", + "minified": 2236, + "gzip": 845, + "brotli": 759 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-current.json b/packages/rescript-relay/benchmarks/results/size-current.json new file mode 100644 index 00000000..3528dc5d --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-current.json @@ -0,0 +1,792 @@ +{ + "fixtureVersion": 1, + "esbuild": "0.25.12", + "node": "v24.16.0", + "artifactCount": 129, + "combined": { + "minified": 277982, + "gzip": 36961, + "brotli": 26651 + }, + "separate": { + "minified": 307794, + "gzip": 119154, + "brotli": 105719 + }, + "files": [ + { + "path": "__tests__/__generated__/GroupAvatar_group_graphql.bs.js", + "minified": 711, + "gzip": 447, + "brotli": 384 + }, + { + "path": "__tests__/__generated__/HasNameComponent_hasName_graphql.bs.js", + "minified": 805, + "gzip": 467, + "brotli": 400 + }, + { + "path": "__tests__/__generated__/LocalUser____relay_model_instance_graphql.bs.js", + "minified": 1059, + "gzip": 579, + "brotli": 506 + }, + { + "path": "__tests__/__generated__/LocalUser__id_graphql.bs.js", + "minified": 747, + "gzip": 462, + "brotli": 399 + }, + { + "path": "__tests__/__generated__/LocalUser_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/Query_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/RichContent_content_graphql.bs.js", + "minified": 722, + "gzip": 447, + "brotli": 380 + }, + { + "path": "__tests__/__generated__/TestAliasedFragmentsQuery_graphql.bs.js", + "minified": 3136, + "gzip": 1125, + "brotli": 1003 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.bs.js", + "minified": 732, + "gzip": 455, + "brotli": 394 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userLastName_graphql.bs.js", + "minified": 730, + "gzip": 454, + "brotli": 391 + }, + { + "path": "__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.bs.js", + "minified": 3002, + "gzip": 1221, + "brotli": 1078 + }, + { + "path": "__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.bs.js", + "minified": 2779, + "gzip": 1170, + "brotli": 1039 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.bs.js", + "minified": 4115, + "gzip": 1392, + "brotli": 1266 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropQuery_graphql.bs.js", + "minified": 3138, + "gzip": 1292, + "brotli": 1158 + }, + { + "path": "__tests__/__generated__/TestCatchMembersPropQuery_graphql.bs.js", + "minified": 3862, + "gzip": 1385, + "brotli": 1245 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMemberQuery_graphql.bs.js", + "minified": 3073, + "gzip": 1217, + "brotli": 1078 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMember_member_graphql.bs.js", + "minified": 1528, + "gzip": 696, + "brotli": 609 + }, + { + "path": "__tests__/__generated__/TestCatchUser_user_graphql.bs.js", + "minified": 913, + "gzip": 545, + "brotli": 474 + }, + { + "path": "__tests__/__generated__/TestCodesplitQuery_graphql.bs.js", + "minified": 7005, + "gzip": 1782, + "brotli": 1615 + }, + { + "path": "__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.bs.js", + "minified": 731, + "gzip": 458, + "brotli": 393 + }, + { + "path": "__tests__/__generated__/TestConnections2_user_graphql.bs.js", + "minified": 4095, + "gzip": 1347, + "brotli": 1226 + }, + { + "path": "__tests__/__generated__/TestConnections3_user_graphql.bs.js", + "minified": 2334, + "gzip": 860, + "brotli": 766 + }, + { + "path": "__tests__/__generated__/TestConnectionsQuery_graphql.bs.js", + "minified": 4221, + "gzip": 1512, + "brotli": 1350 + }, + { + "path": "__tests__/__generated__/TestConnections_AddFriendMutation_graphql.bs.js", + "minified": 2252, + "gzip": 914, + "brotli": 809 + }, + { + "path": "__tests__/__generated__/TestConnections_user_graphql.bs.js", + "minified": 2999, + "gzip": 1075, + "brotli": 966 + }, + { + "path": "__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2735, + "gzip": 1126, + "brotli": 1001 + }, + { + "path": "__tests__/__generated__/TestCustomScalarsQuery_graphql.bs.js", + "minified": 4390, + "gzip": 1486, + "brotli": 1342 + }, + { + "path": "__tests__/__generated__/TestFragmentQuery_graphql.bs.js", + "minified": 4301, + "gzip": 1367, + "brotli": 1223 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.bs.js", + "minified": 903, + "gzip": 534, + "brotli": 463 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredQuery_graphql.bs.js", + "minified": 3467, + "gzip": 1182, + "brotli": 1050 + }, + { + "path": "__tests__/__generated__/TestFragmentRequired_user_graphql.bs.js", + "minified": 890, + "gzip": 525, + "brotli": 452 + }, + { + "path": "__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.bs.js", + "minified": 925, + "gzip": 524, + "brotli": 451 + }, + { + "path": "__tests__/__generated__/TestFragment_inline_graphql.bs.js", + "minified": 694, + "gzip": 430, + "brotli": 372 + }, + { + "path": "__tests__/__generated__/TestFragment_plural_user_graphql.bs.js", + "minified": 996, + "gzip": 531, + "brotli": 458 + }, + { + "path": "__tests__/__generated__/TestFragment_sub_user_graphql.bs.js", + "minified": 718, + "gzip": 450, + "brotli": 387 + }, + { + "path": "__tests__/__generated__/TestFragment_user_graphql.bs.js", + "minified": 1168, + "gzip": 600, + "brotli": 513 + }, + { + "path": "__tests__/__generated__/TestInputUnionQuery_graphql.bs.js", + "minified": 2163, + "gzip": 1037, + "brotli": 904 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadQuery_graphql.bs.js", + "minified": 5414, + "gzip": 1571, + "brotli": 1420 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.bs.js", + "minified": 6100, + "gzip": 1702, + "brotli": 1542 + }, + { + "path": "__tests__/__generated__/TestLocalPayload_user_graphql.bs.js", + "minified": 2552, + "gzip": 853, + "brotli": 758 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.bs.js", + "minified": 2185, + "gzip": 961, + "brotli": 844 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.bs.js", + "minified": 2744, + "gzip": 1172, + "brotli": 1043 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.bs.js", + "minified": 2337, + "gzip": 1033, + "brotli": 913 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.bs.js", + "minified": 2325, + "gzip": 1033, + "brotli": 911 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.bs.js", + "minified": 2183, + "gzip": 956, + "brotli": 844 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.bs.js", + "minified": 2740, + "gzip": 1170, + "brotli": 1047 + }, + { + "path": "__tests__/__generated__/TestMutationInline_user_graphql.bs.js", + "minified": 698, + "gzip": 434, + "brotli": 376 + }, + { + "path": "__tests__/__generated__/TestMutationProvidedVariable_user_graphql.bs.js", + "minified": 898, + "gzip": 514, + "brotli": 443 + }, + { + "path": "__tests__/__generated__/TestMutationQuery_graphql.bs.js", + "minified": 3201, + "gzip": 1208, + "brotli": 1066 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.bs.js", + "minified": 2444, + "gzip": 993, + "brotli": 885 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.bs.js", + "minified": 2994, + "gzip": 1025, + "brotli": 923 + }, + { + "path": "__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 2929, + "gzip": 1004, + "brotli": 900 + }, + { + "path": "__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 4030, + "gzip": 1237, + "brotli": 1123 + }, + { + "path": "__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.bs.js", + "minified": 2788, + "gzip": 967, + "brotli": 880 + }, + { + "path": "__tests__/__generated__/TestMutation_user_graphql.bs.js", + "minified": 1789, + "gzip": 735, + "brotli": 647 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.bs.js", + "minified": 3173, + "gzip": 1240, + "brotli": 1113 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceQuery_graphql.bs.js", + "minified": 3852, + "gzip": 1327, + "brotli": 1196 + }, + { + "path": "__tests__/__generated__/TestNodeInterface_user_graphql.bs.js", + "minified": 720, + "gzip": 454, + "brotli": 387 + }, + { + "path": "__tests__/__generated__/TestNonReactQuery_graphql.bs.js", + "minified": 2468, + "gzip": 1077, + "brotli": 944 + }, + { + "path": "__tests__/__generated__/TestNonReact_user_graphql.bs.js", + "minified": 914, + "gzip": 519, + "brotli": 447 + }, + { + "path": "__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2859, + "gzip": 1175, + "brotli": 1051 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesMutation_graphql.bs.js", + "minified": 2751, + "gzip": 1018, + "brotli": 904 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesQuery_graphql.bs.js", + "minified": 2141, + "gzip": 948, + "brotli": 834 + }, + { + "path": "__tests__/__generated__/TestObserverQuery_graphql.bs.js", + "minified": 1893, + "gzip": 922, + "brotli": 813 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeQuery_graphql.bs.js", + "minified": 4371, + "gzip": 1458, + "brotli": 1305 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.bs.js", + "minified": 5073, + "gzip": 1630, + "brotli": 1473 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_query_graphql.bs.js", + "minified": 2756, + "gzip": 1055, + "brotli": 940 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_user_graphql.bs.js", + "minified": 1062, + "gzip": 549, + "brotli": 475 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionQuery_graphql.bs.js", + "minified": 4940, + "gzip": 1564, + "brotli": 1406 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.bs.js", + "minified": 5708, + "gzip": 1728, + "brotli": 1559 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_query_graphql.bs.js", + "minified": 3869, + "gzip": 1254, + "brotli": 1126 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_user_graphql.bs.js", + "minified": 993, + "gzip": 541, + "brotli": 471 + }, + { + "path": "__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.bs.js", + "minified": 2847, + "gzip": 1122, + "brotli": 989 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.bs.js", + "minified": 3657, + "gzip": 1295, + "brotli": 1149 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.bs.js", + "minified": 4215, + "gzip": 1439, + "brotli": 1293 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.bs.js", + "minified": 1106, + "gzip": 555, + "brotli": 486 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user_graphql.bs.js", + "minified": 2208, + "gzip": 879, + "brotli": 776 + }, + { + "path": "__tests__/__generated__/TestProvidedVariablesQuery_graphql.bs.js", + "minified": 5725, + "gzip": 1542, + "brotli": 1400 + }, + { + "path": "__tests__/__generated__/TestProvidedVariables_user_graphql.bs.js", + "minified": 1581, + "gzip": 572, + "brotli": 504 + }, + { + "path": "__tests__/__generated__/TestQueryInputObjectsQuery_graphql.bs.js", + "minified": 2084, + "gzip": 1030, + "brotli": 911 + }, + { + "path": "__tests__/__generated__/TestQuery_graphql.bs.js", + "minified": 2742, + "gzip": 1179, + "brotli": 1068 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeQuery_graphql.bs.js", + "minified": 3843, + "gzip": 1422, + "brotli": 1281 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.bs.js", + "minified": 3912, + "gzip": 1420, + "brotli": 1275 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNode_user_graphql.bs.js", + "minified": 1705, + "gzip": 769, + "brotli": 668 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.bs.js", + "minified": 2193, + "gzip": 1016, + "brotli": 902 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgs_query_graphql.bs.js", + "minified": 988, + "gzip": 558, + "brotli": 485 + }, + { + "path": "__tests__/__generated__/TestRefetchingQuery_graphql.bs.js", + "minified": 3791, + "gzip": 1391, + "brotli": 1245 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.bs.js", + "minified": 2235, + "gzip": 1022, + "brotli": 905 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.bs.js", + "minified": 1000, + "gzip": 561, + "brotli": 489 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordQuery_graphql.bs.js", + "minified": 3833, + "gzip": 1397, + "brotli": 1244 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.bs.js", + "minified": 4626, + "gzip": 1600, + "brotli": 1448 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecord_user_graphql.bs.js", + "minified": 2166, + "gzip": 874, + "brotli": 768 + }, + { + "path": "__tests__/__generated__/TestRefetchingRefetchQuery_graphql.bs.js", + "minified": 4584, + "gzip": 1596, + "brotli": 1446 + }, + { + "path": "__tests__/__generated__/TestRefetching_user_graphql.bs.js", + "minified": 2154, + "gzip": 870, + "brotli": 765 + }, + { + "path": "__tests__/__generated__/TestRelayResolversAllQuery_graphql.bs.js", + "minified": 5361, + "gzip": 1536, + "brotli": 1386 + }, + { + "path": "__tests__/__generated__/TestRelayResolversQuery_graphql.bs.js", + "minified": 3046, + "gzip": 1181, + "brotli": 1052 + }, + { + "path": "__tests__/__generated__/TestRelayResolvers_user_graphql.bs.js", + "minified": 1300, + "gzip": 625, + "brotli": 542 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver2_graphql.bs.js", + "minified": 794, + "gzip": 465, + "brotli": 395 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver_graphql.bs.js", + "minified": 793, + "gzip": 463, + "brotli": 395 + }, + { + "path": "__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.bs.js", + "minified": 2191, + "gzip": 967, + "brotli": 855 + }, + { + "path": "__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.bs.js", + "minified": 1836, + "gzip": 844, + "brotli": 740 + }, + { + "path": "__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.bs.js", + "minified": 1925, + "gzip": 834, + "brotli": 728 + }, + { + "path": "__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.bs.js", + "minified": 902, + "gzip": 515, + "brotli": 443 + }, + { + "path": "__tests__/__generated__/TestSubscriptionQuery_graphql.bs.js", + "minified": 2542, + "gzip": 1073, + "brotli": 942 + }, + { + "path": "__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.bs.js", + "minified": 2552, + "gzip": 952, + "brotli": 837 + }, + { + "path": "__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.bs.js", + "minified": 3038, + "gzip": 1066, + "brotli": 953 + }, + { + "path": "__tests__/__generated__/TestSubscription_user_graphql.bs.js", + "minified": 1061, + "gzip": 535, + "brotli": 455 + }, + { + "path": "__tests__/__generated__/TestTestingHelpersQuery_graphql.bs.js", + "minified": 3448, + "gzip": 1298, + "brotli": 1170 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_plural_user_graphql.bs.js", + "minified": 1002, + "gzip": 538, + "brotli": 466 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_sub_user_graphql.bs.js", + "minified": 724, + "gzip": 458, + "brotli": 390 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_user_graphql.bs.js", + "minified": 1180, + "gzip": 607, + "brotli": 521 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentQuery_graphql.bs.js", + "minified": 3556, + "gzip": 1264, + "brotli": 1124 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentUser_user_graphql.bs.js", + "minified": 923, + "gzip": 520, + "brotli": 448 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_member_graphql.bs.js", + "minified": 1561, + "gzip": 697, + "brotli": 608 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_plural_member_graphql.bs.js", + "minified": 1575, + "gzip": 709, + "brotli": 623 + }, + { + "path": "__tests__/__generated__/TestUnionsQuery_graphql.bs.js", + "minified": 5235, + "gzip": 1525, + "brotli": 1383 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.bs.js", + "minified": 2816, + "gzip": 1121, + "brotli": 997 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_query_graphql.bs.js", + "minified": 1248, + "gzip": 607, + "brotli": 517 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_updatableUser_graphql.bs.js", + "minified": 678, + "gzip": 363, + "brotli": 321 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.bs.js", + "minified": 1298, + "gzip": 610, + "brotli": 529 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_graphql.bs.js", + "minified": 2607, + "gzip": 1046, + "brotli": 936 + }, + { + "path": "__tests__/__generated__/UserAvatar_user_graphql.bs.js", + "minified": 1049, + "gzip": 572, + "brotli": 494 + }, + { + "path": "__tests__/__generated__/UserMeta____relay_model_instance_graphql.bs.js", + "minified": 785, + "gzip": 476, + "brotli": 404 + }, + { + "path": "__tests__/__generated__/UserMeta_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/UserName_user_graphql.bs.js", + "minified": 785, + "gzip": 458, + "brotli": 393 + }, + { + "path": "__tests__/__generated__/User_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.bs.js", + "minified": 1418, + "gzip": 570, + "brotli": 499 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.bs.js", + "minified": 5142, + "gzip": 1500, + "brotli": 1351 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.bs.js", + "minified": 2295, + "gzip": 849, + "brotli": 767 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-original.json b/packages/rescript-relay/benchmarks/results/size-original.json new file mode 100644 index 00000000..dbdcdfde --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-original.json @@ -0,0 +1,792 @@ +{ + "fixtureVersion": 1, + "esbuild": "0.25.12", + "node": "v24.16.0", + "artifactCount": 129, + "combined": { + "minified": 284957, + "gzip": 37573, + "brotli": 26976 + }, + "separate": { + "minified": 314589, + "gzip": 119093, + "brotli": 106076 + }, + "files": [ + { + "path": "__tests__/__generated__/GroupAvatar_group_graphql.bs.js", + "minified": 765, + "gzip": 464, + "brotli": 396 + }, + { + "path": "__tests__/__generated__/HasNameComponent_hasName_graphql.bs.js", + "minified": 859, + "gzip": 484, + "brotli": 418 + }, + { + "path": "__tests__/__generated__/LocalUser____relay_model_instance_graphql.bs.js", + "minified": 1113, + "gzip": 598, + "brotli": 521 + }, + { + "path": "__tests__/__generated__/LocalUser__id_graphql.bs.js", + "minified": 801, + "gzip": 480, + "brotli": 406 + }, + { + "path": "__tests__/__generated__/LocalUser_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/Query_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/RichContent_content_graphql.bs.js", + "minified": 776, + "gzip": 464, + "brotli": 396 + }, + { + "path": "__tests__/__generated__/TestAliasedFragmentsQuery_graphql.bs.js", + "minified": 3290, + "gzip": 1170, + "brotli": 1048 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.bs.js", + "minified": 786, + "gzip": 475, + "brotli": 409 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userLastName_graphql.bs.js", + "minified": 784, + "gzip": 474, + "brotli": 408 + }, + { + "path": "__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.bs.js", + "minified": 2943, + "gzip": 1200, + "brotli": 1060 + }, + { + "path": "__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.bs.js", + "minified": 2882, + "gzip": 1166, + "brotli": 1042 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.bs.js", + "minified": 4316, + "gzip": 1383, + "brotli": 1260 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropQuery_graphql.bs.js", + "minified": 3265, + "gzip": 1286, + "brotli": 1152 + }, + { + "path": "__tests__/__generated__/TestCatchMembersPropQuery_graphql.bs.js", + "minified": 3983, + "gzip": 1364, + "brotli": 1234 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMemberQuery_graphql.bs.js", + "minified": 3143, + "gzip": 1208, + "brotli": 1078 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMember_member_graphql.bs.js", + "minified": 1494, + "gzip": 677, + "brotli": 592 + }, + { + "path": "__tests__/__generated__/TestCatchUser_user_graphql.bs.js", + "minified": 894, + "gzip": 529, + "brotli": 456 + }, + { + "path": "__tests__/__generated__/TestCodesplitQuery_graphql.bs.js", + "minified": 7159, + "gzip": 1825, + "brotli": 1649 + }, + { + "path": "__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.bs.js", + "minified": 785, + "gzip": 477, + "brotli": 405 + }, + { + "path": "__tests__/__generated__/TestConnections2_user_graphql.bs.js", + "minified": 4019, + "gzip": 1315, + "brotli": 1192 + }, + { + "path": "__tests__/__generated__/TestConnections3_user_graphql.bs.js", + "minified": 2269, + "gzip": 830, + "brotli": 746 + }, + { + "path": "__tests__/__generated__/TestConnectionsQuery_graphql.bs.js", + "minified": 4236, + "gzip": 1504, + "brotli": 1353 + }, + { + "path": "__tests__/__generated__/TestConnections_AddFriendMutation_graphql.bs.js", + "minified": 2342, + "gzip": 898, + "brotli": 796 + }, + { + "path": "__tests__/__generated__/TestConnections_user_graphql.bs.js", + "minified": 2949, + "gzip": 1045, + "brotli": 953 + }, + { + "path": "__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2861, + "gzip": 1121, + "brotli": 1003 + }, + { + "path": "__tests__/__generated__/TestCustomScalarsQuery_graphql.bs.js", + "minified": 4455, + "gzip": 1475, + "brotli": 1336 + }, + { + "path": "__tests__/__generated__/TestFragmentQuery_graphql.bs.js", + "minified": 4348, + "gzip": 1341, + "brotli": 1205 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.bs.js", + "minified": 957, + "gzip": 550, + "brotli": 480 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredQuery_graphql.bs.js", + "minified": 3514, + "gzip": 1157, + "brotli": 1038 + }, + { + "path": "__tests__/__generated__/TestFragmentRequired_user_graphql.bs.js", + "minified": 944, + "gzip": 542, + "brotli": 469 + }, + { + "path": "__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.bs.js", + "minified": 979, + "gzip": 541, + "brotli": 465 + }, + { + "path": "__tests__/__generated__/TestFragment_inline_graphql.bs.js", + "minified": 748, + "gzip": 449, + "brotli": 388 + }, + { + "path": "__tests__/__generated__/TestFragment_plural_user_graphql.bs.js", + "minified": 1050, + "gzip": 548, + "brotli": 476 + }, + { + "path": "__tests__/__generated__/TestFragment_sub_user_graphql.bs.js", + "minified": 772, + "gzip": 469, + "brotli": 404 + }, + { + "path": "__tests__/__generated__/TestFragment_user_graphql.bs.js", + "minified": 1157, + "gzip": 587, + "brotli": 505 + }, + { + "path": "__tests__/__generated__/TestInputUnionQuery_graphql.bs.js", + "minified": 2224, + "gzip": 1034, + "brotli": 907 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadQuery_graphql.bs.js", + "minified": 5717, + "gzip": 1560, + "brotli": 1416 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.bs.js", + "minified": 6391, + "gzip": 1697, + "brotli": 1559 + }, + { + "path": "__tests__/__generated__/TestLocalPayload_user_graphql.bs.js", + "minified": 2499, + "gzip": 825, + "brotli": 735 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.bs.js", + "minified": 2339, + "gzip": 1005, + "brotli": 891 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.bs.js", + "minified": 2823, + "gzip": 1170, + "brotli": 1061 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.bs.js", + "minified": 2362, + "gzip": 1007, + "brotli": 900 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.bs.js", + "minified": 2346, + "gzip": 1006, + "brotli": 895 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.bs.js", + "minified": 2337, + "gzip": 1001, + "brotli": 887 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.bs.js", + "minified": 2819, + "gzip": 1170, + "brotli": 1044 + }, + { + "path": "__tests__/__generated__/TestMutationInline_user_graphql.bs.js", + "minified": 752, + "gzip": 454, + "brotli": 393 + }, + { + "path": "__tests__/__generated__/TestMutationProvidedVariable_user_graphql.bs.js", + "minified": 952, + "gzip": 530, + "brotli": 458 + }, + { + "path": "__tests__/__generated__/TestMutationQuery_graphql.bs.js", + "minified": 3277, + "gzip": 1196, + "brotli": 1066 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.bs.js", + "minified": 2488, + "gzip": 982, + "brotli": 884 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.bs.js", + "minified": 3238, + "gzip": 1032, + "brotli": 929 + }, + { + "path": "__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 3173, + "gzip": 1014, + "brotli": 912 + }, + { + "path": "__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 4241, + "gzip": 1223, + "brotli": 1110 + }, + { + "path": "__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.bs.js", + "minified": 2897, + "gzip": 959, + "brotli": 858 + }, + { + "path": "__tests__/__generated__/TestMutation_user_graphql.bs.js", + "minified": 1766, + "gzip": 708, + "brotli": 646 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.bs.js", + "minified": 3252, + "gzip": 1238, + "brotli": 1114 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceQuery_graphql.bs.js", + "minified": 3981, + "gzip": 1323, + "brotli": 1195 + }, + { + "path": "__tests__/__generated__/TestNodeInterface_user_graphql.bs.js", + "minified": 774, + "gzip": 473, + "brotli": 403 + }, + { + "path": "__tests__/__generated__/TestNonReactQuery_graphql.bs.js", + "minified": 2544, + "gzip": 1068, + "brotli": 944 + }, + { + "path": "__tests__/__generated__/TestNonReact_user_graphql.bs.js", + "minified": 968, + "gzip": 536, + "brotli": 463 + }, + { + "path": "__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2952, + "gzip": 1167, + "brotli": 1046 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesMutation_graphql.bs.js", + "minified": 2826, + "gzip": 1017, + "brotli": 921 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesQuery_graphql.bs.js", + "minified": 2295, + "gzip": 993, + "brotli": 884 + }, + { + "path": "__tests__/__generated__/TestObserverQuery_graphql.bs.js", + "minified": 2047, + "gzip": 966, + "brotli": 861 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeQuery_graphql.bs.js", + "minified": 4439, + "gzip": 1448, + "brotli": 1298 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.bs.js", + "minified": 5058, + "gzip": 1616, + "brotli": 1469 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_query_graphql.bs.js", + "minified": 2693, + "gzip": 1032, + "brotli": 922 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_user_graphql.bs.js", + "minified": 1116, + "gzip": 564, + "brotli": 486 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionQuery_graphql.bs.js", + "minified": 5010, + "gzip": 1553, + "brotli": 1395 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.bs.js", + "minified": 5695, + "gzip": 1714, + "brotli": 1548 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_query_graphql.bs.js", + "minified": 3716, + "gzip": 1206, + "brotli": 1087 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_user_graphql.bs.js", + "minified": 1047, + "gzip": 556, + "brotli": 485 + }, + { + "path": "__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.bs.js", + "minified": 2929, + "gzip": 1112, + "brotli": 1002 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.bs.js", + "minified": 3733, + "gzip": 1284, + "brotli": 1157 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.bs.js", + "minified": 4283, + "gzip": 1429, + "brotli": 1280 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.bs.js", + "minified": 1160, + "gzip": 572, + "brotli": 504 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user_graphql.bs.js", + "minified": 2184, + "gzip": 859, + "brotli": 762 + }, + { + "path": "__tests__/__generated__/TestProvidedVariablesQuery_graphql.bs.js", + "minified": 5584, + "gzip": 1523, + "brotli": 1384 + }, + { + "path": "__tests__/__generated__/TestProvidedVariables_user_graphql.bs.js", + "minified": 1635, + "gzip": 588, + "brotli": 512 + }, + { + "path": "__tests__/__generated__/TestQueryInputObjectsQuery_graphql.bs.js", + "minified": 2156, + "gzip": 1014, + "brotli": 890 + }, + { + "path": "__tests__/__generated__/TestQuery_graphql.bs.js", + "minified": 2767, + "gzip": 1149, + "brotli": 1036 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeQuery_graphql.bs.js", + "minified": 3826, + "gzip": 1405, + "brotli": 1266 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.bs.js", + "minified": 3890, + "gzip": 1401, + "brotli": 1260 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNode_user_graphql.bs.js", + "minified": 1759, + "gzip": 787, + "brotli": 682 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.bs.js", + "minified": 2263, + "gzip": 1007, + "brotli": 896 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgs_query_graphql.bs.js", + "minified": 1042, + "gzip": 573, + "brotli": 500 + }, + { + "path": "__tests__/__generated__/TestRefetchingQuery_graphql.bs.js", + "minified": 3792, + "gzip": 1385, + "brotli": 1234 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.bs.js", + "minified": 2305, + "gzip": 1013, + "brotli": 902 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.bs.js", + "minified": 1054, + "gzip": 579, + "brotli": 503 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordQuery_graphql.bs.js", + "minified": 3834, + "gzip": 1391, + "brotli": 1241 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.bs.js", + "minified": 4579, + "gzip": 1577, + "brotli": 1429 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecord_user_graphql.bs.js", + "minified": 2134, + "gzip": 846, + "brotli": 748 + }, + { + "path": "__tests__/__generated__/TestRefetchingRefetchQuery_graphql.bs.js", + "minified": 4537, + "gzip": 1572, + "brotli": 1424 + }, + { + "path": "__tests__/__generated__/TestRefetching_user_graphql.bs.js", + "minified": 2122, + "gzip": 841, + "brotli": 734 + }, + { + "path": "__tests__/__generated__/TestRelayResolversAllQuery_graphql.bs.js", + "minified": 5515, + "gzip": 1578, + "brotli": 1429 + }, + { + "path": "__tests__/__generated__/TestRelayResolversQuery_graphql.bs.js", + "minified": 3122, + "gzip": 1173, + "brotli": 1055 + }, + { + "path": "__tests__/__generated__/TestRelayResolvers_user_graphql.bs.js", + "minified": 1354, + "gzip": 643, + "brotli": 563 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver2_graphql.bs.js", + "minified": 848, + "gzip": 480, + "brotli": 412 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver_graphql.bs.js", + "minified": 847, + "gzip": 478, + "brotli": 410 + }, + { + "path": "__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.bs.js", + "minified": 2345, + "gzip": 1009, + "brotli": 924 + }, + { + "path": "__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.bs.js", + "minified": 1939, + "gzip": 845, + "brotli": 743 + }, + { + "path": "__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.bs.js", + "minified": 1997, + "gzip": 827, + "brotli": 760 + }, + { + "path": "__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.bs.js", + "minified": 956, + "gzip": 532, + "brotli": 455 + }, + { + "path": "__tests__/__generated__/TestSubscriptionQuery_graphql.bs.js", + "minified": 2618, + "gzip": 1064, + "brotli": 938 + }, + { + "path": "__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.bs.js", + "minified": 2591, + "gzip": 934, + "brotli": 836 + }, + { + "path": "__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.bs.js", + "minified": 3077, + "gzip": 1051, + "brotli": 930 + }, + { + "path": "__tests__/__generated__/TestSubscription_user_graphql.bs.js", + "minified": 1115, + "gzip": 551, + "brotli": 473 + }, + { + "path": "__tests__/__generated__/TestTestingHelpersQuery_graphql.bs.js", + "minified": 3660, + "gzip": 1309, + "brotli": 1178 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_plural_user_graphql.bs.js", + "minified": 1056, + "gzip": 556, + "brotli": 484 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_sub_user_graphql.bs.js", + "minified": 778, + "gzip": 476, + "brotli": 409 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_user_graphql.bs.js", + "minified": 1169, + "gzip": 593, + "brotli": 512 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentQuery_graphql.bs.js", + "minified": 3626, + "gzip": 1255, + "brotli": 1119 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentUser_user_graphql.bs.js", + "minified": 977, + "gzip": 539, + "brotli": 462 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_member_graphql.bs.js", + "minified": 1532, + "gzip": 675, + "brotli": 596 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_plural_member_graphql.bs.js", + "minified": 1546, + "gzip": 688, + "brotli": 604 + }, + { + "path": "__tests__/__generated__/TestUnionsQuery_graphql.bs.js", + "minified": 5357, + "gzip": 1507, + "brotli": 1371 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.bs.js", + "minified": 2886, + "gzip": 1113, + "brotli": 993 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_query_graphql.bs.js", + "minified": 1233, + "gzip": 592, + "brotli": 509 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_updatableUser_graphql.bs.js", + "minified": 678, + "gzip": 363, + "brotli": 321 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.bs.js", + "minified": 1328, + "gzip": 621, + "brotli": 542 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_graphql.bs.js", + "minified": 2761, + "gzip": 1088, + "brotli": 982 + }, + { + "path": "__tests__/__generated__/UserAvatar_user_graphql.bs.js", + "minified": 1103, + "gzip": 588, + "brotli": 509 + }, + { + "path": "__tests__/__generated__/UserMeta____relay_model_instance_graphql.bs.js", + "minified": 839, + "gzip": 491, + "brotli": 423 + }, + { + "path": "__tests__/__generated__/UserMeta_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/UserName_user_graphql.bs.js", + "minified": 839, + "gzip": 473, + "brotli": 408 + }, + { + "path": "__tests__/__generated__/User_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.bs.js", + "minified": 1472, + "gzip": 586, + "brotli": 508 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.bs.js", + "minified": 5021, + "gzip": 1469, + "brotli": 1326 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.bs.js", + "minified": 2144, + "gzip": 820, + "brotli": 731 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-perf-forward-before.json b/packages/rescript-relay/benchmarks/results/size-perf-forward-before.json new file mode 100644 index 00000000..519fab7b --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-perf-forward-before.json @@ -0,0 +1,102 @@ +{ + "fixtureVersion": 3, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "/tmp/relay-conversion-audit/size/prior-runtime/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "preparationNs": 9776.902999999998, + "medianNs": 203.18765624999986, + "p10Ns": 193.04925292968733, + "p90Ns": 286.70808593749985, + "opsPerSecond": 4921558.81147431 + }, + { + "name": "unchanged object", + "iterations": 409600, + "newOutputObjects": 0, + "preparationNs": 4128.35199999995, + "medianNs": 110.96232910156269, + "p10Ns": 93.99455078125007, + "p90Ns": 140.87839111328105, + "opsPerSecond": 9012067.50161769 + }, + { + "name": "nullable query", + "iterations": 204800, + "newOutputObjects": 3, + "preparationNs": 1431.5590000001066, + "medianNs": 202.2801611328129, + "p10Ns": 148.0475488281252, + "p90Ns": 311.5227929687492, + "opsPerSecond": 4943638.537757645 + }, + { + "name": "connection / 100 rows", + "iterations": 3200, + "newOutputObjects": 403, + "preparationNs": 4543.11000000007, + "medianNs": 42138.94937500001, + "p10Ns": 27849.995000000123, + "p90Ns": 53245.995000000046, + "opsPerSecond": 23731.0140578226 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 3200, + "newOutputObjects": 301, + "preparationNs": 1755.2140000007057, + "medianNs": 32837.35593750009, + "p10Ns": 15530.37281249999, + "p90Ns": 38517.774375000045, + "opsPerSecond": 30453.121801381247 + }, + { + "name": "union list / 100 rows", + "iterations": 1600, + "newOutputObjects": 302, + "preparationNs": 2868.4990000001567, + "medianNs": 37804.71250000005, + "p10Ns": 33257.77625, + "p90Ns": 45178.288749999636, + "opsPerSecond": 26451.728736199188 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "preparationNs": 3524.945999999545, + "medianNs": 20678.589375000058, + "p10Ns": 13515.776874999972, + "p90Ns": 41989.59562499965, + "opsPerSecond": 48359.198099314126 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 0, + "preparationNs": 4554.2310000000725, + "medianNs": 666.3554199218602, + "p10Ns": 481.41083984376104, + "p90Ns": 966.0760937499902, + "opsPerSecond": 1500700.6322800894 + }, + { + "name": "recursive inputs", + "iterations": 1600, + "newOutputObjects": 103, + "preparationNs": 8457.033999999112, + "medianNs": 31526.472499999727, + "p10Ns": 20369.229374999806, + "p90Ns": 48759.51250000071, + "opsPerSecond": 31719.37488407587 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-perf-forward-current.json b/packages/rescript-relay/benchmarks/results/size-perf-forward-current.json new file mode 100644 index 00000000..3ecff8a8 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-perf-forward-current.json @@ -0,0 +1,102 @@ +{ + "fixtureVersion": 3, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 204800, + "newOutputObjects": 3, + "preparationNs": 17575.714999999997, + "medianNs": 115.46565917968765, + "p10Ns": 109.94398925781246, + "p90Ns": 217.77763671875007, + "opsPerSecond": 8660583.649756853 + }, + { + "name": "unchanged object", + "iterations": 409600, + "newOutputObjects": 0, + "preparationNs": 0, + "medianNs": 66.43720947265619, + "p10Ns": 57.158791503906244, + "p90Ns": 186.7120776367187, + "opsPerSecond": 15051806.17815644 + }, + { + "name": "nullable query", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 0, + "medianNs": 167.93248046874984, + "p10Ns": 150.55474609374951, + "p90Ns": 261.51394775390634, + "opsPerSecond": 5954774.187868247 + }, + { + "name": "connection / 100 rows", + "iterations": 3200, + "newOutputObjects": 403, + "preparationNs": 3672.4960000001374, + "medianNs": 26391.065625000036, + "p10Ns": 23787.179374999996, + "p90Ns": 45069.14625000008, + "opsPerSecond": 37891.61128274822 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 1600, + "newOutputObjects": 301, + "preparationNs": 2230.7369999998627, + "medianNs": 19132.703125000036, + "p10Ns": 16630.06875000008, + "p90Ns": 33063.09437500033, + "opsPerSecond": 52266.52990257999 + }, + { + "name": "union list / 100 rows", + "iterations": 3200, + "newOutputObjects": 302, + "preparationNs": 3426.3180000007196, + "medianNs": 24334.706250000127, + "p10Ns": 16858.60843750021, + "p90Ns": 37503.3775, + "opsPerSecond": 41093.57186097099 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "preparationNs": 2098.8369999995484, + "medianNs": 12521.882031250016, + "p10Ns": 11703.86609375001, + "p90Ns": 17578.603593750016, + "opsPerSecond": 79860.19972911161 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 0, + "preparationNs": 1653.4209999999803, + "medianNs": 470.69791992187857, + "p10Ns": 430.332587890625, + "p90Ns": 794.3403710937513, + "opsPerSecond": 2124504.82076906 + }, + { + "name": "recursive inputs", + "iterations": 3200, + "newOutputObjects": 103, + "preparationNs": 4278.997999999774, + "medianNs": 23955.590625000124, + "p10Ns": 17811.185625000122, + "p90Ns": 29746.338749999948, + "opsPerSecond": 41743.909204910065 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-perf-reverse-before.json b/packages/rescript-relay/benchmarks/results/size-perf-reverse-before.json new file mode 100644 index 00000000..d8a1ad77 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-perf-reverse-before.json @@ -0,0 +1,102 @@ +{ + "fixtureVersion": 3, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "/tmp/relay-conversion-audit/size/prior-runtime/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 4196.099, + "medianNs": 117.18968017578123, + "p10Ns": 115.08749267578125, + "p90Ns": 160.97857666015625, + "opsPerSecond": 8533174.58073124 + }, + { + "name": "unchanged object", + "iterations": 819200, + "newOutputObjects": 0, + "preparationNs": 1343.170999999984, + "medianNs": 59.47453613281251, + "p10Ns": 56.43236938476542, + "p90Ns": 67.88435668945326, + "opsPerSecond": 16813918.443464633 + }, + { + "name": "nullable query", + "iterations": 204800, + "newOutputObjects": 3, + "preparationNs": 940.7770000000255, + "medianNs": 156.74259765624976, + "p10Ns": 147.97614257812563, + "p90Ns": 210.13202636718688, + "opsPerSecond": 6379886.609976234 + }, + { + "name": "connection / 100 rows", + "iterations": 3200, + "newOutputObjects": 403, + "preparationNs": 10279.673000000003, + "medianNs": 19944.135625000003, + "p10Ns": 19355.123437500053, + "p90Ns": 24975.94500000005, + "opsPerSecond": 50140.05213374595 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 3200, + "newOutputObjects": 301, + "preparationNs": 1270.542999999634, + "medianNs": 14908.731875, + "p10Ns": 13966.044999999951, + "p90Ns": 19967.02875000011, + "opsPerSecond": 67074.78599684723 + }, + { + "name": "union list / 100 rows", + "iterations": 3200, + "newOutputObjects": 302, + "preparationNs": 8542.061999999532, + "medianNs": 18627.90093749993, + "p10Ns": 17898.641562499906, + "p90Ns": 24174.324687500073, + "opsPerSecond": 53682.91378374761 + }, + { + "name": "custom scalar list", + "iterations": 6400, + "newOutputObjects": 102, + "preparationNs": 1914.6059999993668, + "medianNs": 11811.361718749966, + "p10Ns": 11393.46203125001, + "p90Ns": 15107.100468750046, + "opsPerSecond": 84664.24310861196 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 0, + "preparationNs": 1720.7579999994778, + "medianNs": 470.8270312500052, + "p10Ns": 441.6747070312521, + "p90Ns": 764.5654296875026, + "opsPerSecond": 2123922.233914833 + }, + { + "name": "recursive inputs", + "iterations": 3200, + "newOutputObjects": 103, + "preparationNs": 3102.8529999994134, + "medianNs": 16371.944062499893, + "p10Ns": 15326.054687499776, + "p90Ns": 21332.098124999848, + "opsPerSecond": 61080.101189113535 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-perf-reverse-current.json b/packages/rescript-relay/benchmarks/results/size-perf-reverse-current.json new file mode 100644 index 00000000..9aeac462 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-perf-reverse-current.json @@ -0,0 +1,102 @@ +{ + "fixtureVersion": 3, + "mode": "prepared", + "node": "v24.16.0", + "platform": "linux x64", + "cpu": "AMD RYZEN AI MAX+ 395 w/ Radeon 8060S", + "samples": 15, + "durationMs": 40, + "implementation": "src/utils.js", + "results": [ + { + "name": "small fragment", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 9097.019, + "medianNs": 119.27940429687479, + "p10Ns": 106.52258300781232, + "p90Ns": 216.27204589843768, + "opsPerSecond": 8383677.013603268 + }, + { + "name": "unchanged object", + "iterations": 819200, + "newOutputObjects": 0, + "preparationNs": 0, + "medianNs": 59.3171533203124, + "p10Ns": 54.28314086914049, + "p90Ns": 80.1415173339845, + "opsPerSecond": 16858529.852233533 + }, + { + "name": "nullable query", + "iterations": 409600, + "newOutputObjects": 3, + "preparationNs": 0, + "medianNs": 214.0196875, + "p10Ns": 153.00184326171862, + "p90Ns": 292.52293945312476, + "opsPerSecond": 4672467.340183365 + }, + { + "name": "connection / 100 rows", + "iterations": 1600, + "newOutputObjects": 403, + "preparationNs": 3614.755000000059, + "medianNs": 25448.43312499978, + "p10Ns": 24583.15812500018, + "p90Ns": 30770.828750000306, + "opsPerSecond": 39295.1501213499 + }, + { + "name": "plural fragment / 100 rows", + "iterations": 400, + "newOutputObjects": 301, + "preparationNs": 1732.0100000006278, + "medianNs": 25535.37249999863, + "p10Ns": 15496.4300000006, + "p90Ns": 26433.52749999849, + "opsPerSecond": 39161.36332062724 + }, + { + "name": "union list / 100 rows", + "iterations": 1600, + "newOutputObjects": 302, + "preparationNs": 6779.786000000058, + "medianNs": 19382.96874999992, + "p10Ns": 18112.0924999999, + "p90Ns": 21482.186874999912, + "opsPerSecond": 51591.68406542492 + }, + { + "name": "custom scalar list", + "iterations": 3200, + "newOutputObjects": 102, + "preparationNs": 1833.1520000001547, + "medianNs": 11957.052812500136, + "p10Ns": 11725.159062499984, + "p90Ns": 13631.066250000005, + "opsPerSecond": 83632.64892119406 + }, + { + "name": "opaque JSON list", + "iterations": 102400, + "newOutputObjects": 0, + "preparationNs": 1684.4590000000608, + "medianNs": 437.20769531250704, + "p10Ns": 425.51439453125005, + "p90Ns": 469.33061523437124, + "opsPerSecond": 2287242.4495758717 + }, + { + "name": "recursive inputs", + "iterations": 3200, + "newOutputObjects": 103, + "preparationNs": 3244.8920000006183, + "medianNs": 17352.02968750002, + "p10Ns": 16154.881249999846, + "p90Ns": 21942.977187499935, + "opsPerSecond": 57630.14575294184 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-pruned-lists.json b/packages/rescript-relay/benchmarks/results/size-pruned-lists.json new file mode 100644 index 00000000..44689418 --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-pruned-lists.json @@ -0,0 +1,792 @@ +{ + "fixtureVersion": 1, + "esbuild": "0.25.12", + "node": "v24.16.0", + "artifactCount": 129, + "combined": { + "minified": 272639, + "gzip": 36319, + "brotli": 26233 + }, + "separate": { + "minified": 302536, + "gzip": 118381, + "brotli": 104973 + }, + "files": [ + { + "path": "__tests__/__generated__/GroupAvatar_group_graphql.bs.js", + "minified": 711, + "gzip": 447, + "brotli": 384 + }, + { + "path": "__tests__/__generated__/HasNameComponent_hasName_graphql.bs.js", + "minified": 805, + "gzip": 467, + "brotli": 400 + }, + { + "path": "__tests__/__generated__/LocalUser____relay_model_instance_graphql.bs.js", + "minified": 1059, + "gzip": 579, + "brotli": 506 + }, + { + "path": "__tests__/__generated__/LocalUser__id_graphql.bs.js", + "minified": 747, + "gzip": 462, + "brotli": 399 + }, + { + "path": "__tests__/__generated__/LocalUser_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/Query_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/RichContent_content_graphql.bs.js", + "minified": 722, + "gzip": 447, + "brotli": 380 + }, + { + "path": "__tests__/__generated__/TestAliasedFragmentsQuery_graphql.bs.js", + "minified": 3136, + "gzip": 1125, + "brotli": 1003 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.bs.js", + "minified": 732, + "gzip": 455, + "brotli": 394 + }, + { + "path": "__tests__/__generated__/TestAliasedFragments_userLastName_graphql.bs.js", + "minified": 730, + "gzip": 454, + "brotli": 391 + }, + { + "path": "__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.bs.js", + "minified": 2792, + "gzip": 1191, + "brotli": 1066 + }, + { + "path": "__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.bs.js", + "minified": 2721, + "gzip": 1165, + "brotli": 1035 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.bs.js", + "minified": 3889, + "gzip": 1386, + "brotli": 1260 + }, + { + "path": "__tests__/__generated__/TestCatchMemberPropQuery_graphql.bs.js", + "minified": 3020, + "gzip": 1285, + "brotli": 1159 + }, + { + "path": "__tests__/__generated__/TestCatchMembersPropQuery_graphql.bs.js", + "minified": 3708, + "gzip": 1381, + "brotli": 1244 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMemberQuery_graphql.bs.js", + "minified": 3073, + "gzip": 1217, + "brotli": 1078 + }, + { + "path": "__tests__/__generated__/TestCatchUnionMember_member_graphql.bs.js", + "minified": 1472, + "gzip": 691, + "brotli": 604 + }, + { + "path": "__tests__/__generated__/TestCatchUser_user_graphql.bs.js", + "minified": 875, + "gzip": 542, + "brotli": 465 + }, + { + "path": "__tests__/__generated__/TestCodesplitQuery_graphql.bs.js", + "minified": 7005, + "gzip": 1782, + "brotli": 1615 + }, + { + "path": "__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.bs.js", + "minified": 731, + "gzip": 458, + "brotli": 393 + }, + { + "path": "__tests__/__generated__/TestConnections2_user_graphql.bs.js", + "minified": 4007, + "gzip": 1328, + "brotli": 1202 + }, + { + "path": "__tests__/__generated__/TestConnections3_user_graphql.bs.js", + "minified": 2215, + "gzip": 817, + "brotli": 726 + }, + { + "path": "__tests__/__generated__/TestConnectionsQuery_graphql.bs.js", + "minified": 4183, + "gzip": 1508, + "brotli": 1348 + }, + { + "path": "__tests__/__generated__/TestConnections_AddFriendMutation_graphql.bs.js", + "minified": 2162, + "gzip": 856, + "brotli": 771 + }, + { + "path": "__tests__/__generated__/TestConnections_user_graphql.bs.js", + "minified": 2895, + "gzip": 1031, + "brotli": 928 + }, + { + "path": "__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2616, + "gzip": 1119, + "brotli": 992 + }, + { + "path": "__tests__/__generated__/TestCustomScalarsQuery_graphql.bs.js", + "minified": 4182, + "gzip": 1475, + "brotli": 1336 + }, + { + "path": "__tests__/__generated__/TestFragmentQuery_graphql.bs.js", + "minified": 4301, + "gzip": 1367, + "brotli": 1223 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.bs.js", + "minified": 903, + "gzip": 534, + "brotli": 463 + }, + { + "path": "__tests__/__generated__/TestFragmentRequiredQuery_graphql.bs.js", + "minified": 3467, + "gzip": 1182, + "brotli": 1050 + }, + { + "path": "__tests__/__generated__/TestFragmentRequired_user_graphql.bs.js", + "minified": 890, + "gzip": 525, + "brotli": 452 + }, + { + "path": "__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.bs.js", + "minified": 925, + "gzip": 524, + "brotli": 451 + }, + { + "path": "__tests__/__generated__/TestFragment_inline_graphql.bs.js", + "minified": 694, + "gzip": 430, + "brotli": 372 + }, + { + "path": "__tests__/__generated__/TestFragment_plural_user_graphql.bs.js", + "minified": 996, + "gzip": 531, + "brotli": 458 + }, + { + "path": "__tests__/__generated__/TestFragment_sub_user_graphql.bs.js", + "minified": 718, + "gzip": 450, + "brotli": 387 + }, + { + "path": "__tests__/__generated__/TestFragment_user_graphql.bs.js", + "minified": 1168, + "gzip": 600, + "brotli": 513 + }, + { + "path": "__tests__/__generated__/TestInputUnionQuery_graphql.bs.js", + "minified": 2163, + "gzip": 1037, + "brotli": 904 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadQuery_graphql.bs.js", + "minified": 5054, + "gzip": 1560, + "brotli": 1408 + }, + { + "path": "__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.bs.js", + "minified": 5686, + "gzip": 1693, + "brotli": 1525 + }, + { + "path": "__tests__/__generated__/TestLocalPayload_user_graphql.bs.js", + "minified": 2408, + "gzip": 847, + "brotli": 753 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.bs.js", + "minified": 2185, + "gzip": 961, + "brotli": 844 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.bs.js", + "minified": 2708, + "gzip": 1169, + "brotli": 1043 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.bs.js", + "minified": 2208, + "gzip": 963, + "brotli": 846 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.bs.js", + "minified": 2192, + "gzip": 963, + "brotli": 843 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.bs.js", + "minified": 2183, + "gzip": 956, + "brotli": 844 + }, + { + "path": "__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.bs.js", + "minified": 2704, + "gzip": 1167, + "brotli": 1040 + }, + { + "path": "__tests__/__generated__/TestMutationInline_user_graphql.bs.js", + "minified": 698, + "gzip": 434, + "brotli": 376 + }, + { + "path": "__tests__/__generated__/TestMutationProvidedVariable_user_graphql.bs.js", + "minified": 898, + "gzip": 514, + "brotli": 443 + }, + { + "path": "__tests__/__generated__/TestMutationQuery_graphql.bs.js", + "minified": 3201, + "gzip": 1208, + "brotli": 1066 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.bs.js", + "minified": 2404, + "gzip": 990, + "brotli": 882 + }, + { + "path": "__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.bs.js", + "minified": 2994, + "gzip": 1025, + "brotli": 923 + }, + { + "path": "__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 2929, + "gzip": 1004, + "brotli": 900 + }, + { + "path": "__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.bs.js", + "minified": 3910, + "gzip": 1231, + "brotli": 1116 + }, + { + "path": "__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.bs.js", + "minified": 2788, + "gzip": 967, + "brotli": 880 + }, + { + "path": "__tests__/__generated__/TestMutation_user_graphql.bs.js", + "minified": 1757, + "gzip": 732, + "brotli": 645 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.bs.js", + "minified": 3137, + "gzip": 1238, + "brotli": 1103 + }, + { + "path": "__tests__/__generated__/TestNodeInterfaceQuery_graphql.bs.js", + "minified": 3720, + "gzip": 1318, + "brotli": 1193 + }, + { + "path": "__tests__/__generated__/TestNodeInterface_user_graphql.bs.js", + "minified": 720, + "gzip": 454, + "brotli": 387 + }, + { + "path": "__tests__/__generated__/TestNonReactQuery_graphql.bs.js", + "minified": 2468, + "gzip": 1077, + "brotli": 944 + }, + { + "path": "__tests__/__generated__/TestNonReact_user_graphql.bs.js", + "minified": 914, + "gzip": 519, + "brotli": 447 + }, + { + "path": "__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.bs.js", + "minified": 2798, + "gzip": 1170, + "brotli": 1035 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesMutation_graphql.bs.js", + "minified": 2713, + "gzip": 1016, + "brotli": 908 + }, + { + "path": "__tests__/__generated__/TestNullableVariablesQuery_graphql.bs.js", + "minified": 2141, + "gzip": 948, + "brotli": 834 + }, + { + "path": "__tests__/__generated__/TestObserverQuery_graphql.bs.js", + "minified": 1893, + "gzip": 922, + "brotli": 813 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeQuery_graphql.bs.js", + "minified": 4371, + "gzip": 1458, + "brotli": 1305 + }, + { + "path": "__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.bs.js", + "minified": 4990, + "gzip": 1625, + "brotli": 1474 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_query_graphql.bs.js", + "minified": 2756, + "gzip": 1055, + "brotli": 940 + }, + { + "path": "__tests__/__generated__/TestPaginationInNode_user_graphql.bs.js", + "minified": 1062, + "gzip": 549, + "brotli": 475 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionQuery_graphql.bs.js", + "minified": 4940, + "gzip": 1564, + "brotli": 1406 + }, + { + "path": "__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.bs.js", + "minified": 5625, + "gzip": 1722, + "brotli": 1554 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_query_graphql.bs.js", + "minified": 3741, + "gzip": 1239, + "brotli": 1113 + }, + { + "path": "__tests__/__generated__/TestPaginationUnion_user_graphql.bs.js", + "minified": 993, + "gzip": 541, + "brotli": 471 + }, + { + "path": "__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.bs.js", + "minified": 2722, + "gzip": 1112, + "brotli": 991 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.bs.js", + "minified": 3657, + "gzip": 1295, + "brotli": 1149 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.bs.js", + "minified": 4215, + "gzip": 1439, + "brotli": 1293 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.bs.js", + "minified": 1106, + "gzip": 555, + "brotli": 486 + }, + { + "path": "__tests__/__generated__/TestPrefetchablePagination_user_graphql.bs.js", + "minified": 2208, + "gzip": 879, + "brotli": 776 + }, + { + "path": "__tests__/__generated__/TestProvidedVariablesQuery_graphql.bs.js", + "minified": 5647, + "gzip": 1538, + "brotli": 1388 + }, + { + "path": "__tests__/__generated__/TestProvidedVariables_user_graphql.bs.js", + "minified": 1581, + "gzip": 572, + "brotli": 504 + }, + { + "path": "__tests__/__generated__/TestQueryInputObjectsQuery_graphql.bs.js", + "minified": 2061, + "gzip": 1018, + "brotli": 892 + }, + { + "path": "__tests__/__generated__/TestQuery_graphql.bs.js", + "minified": 2613, + "gzip": 1106, + "brotli": 978 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeQuery_graphql.bs.js", + "minified": 3713, + "gzip": 1409, + "brotli": 1274 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.bs.js", + "minified": 3822, + "gzip": 1412, + "brotli": 1264 + }, + { + "path": "__tests__/__generated__/TestRefetchingInNode_user_graphql.bs.js", + "minified": 1705, + "gzip": 769, + "brotli": 668 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.bs.js", + "minified": 2193, + "gzip": 1016, + "brotli": 902 + }, + { + "path": "__tests__/__generated__/TestRefetchingNoArgs_query_graphql.bs.js", + "minified": 988, + "gzip": 558, + "brotli": 485 + }, + { + "path": "__tests__/__generated__/TestRefetchingQuery_graphql.bs.js", + "minified": 3719, + "gzip": 1387, + "brotli": 1234 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.bs.js", + "minified": 2235, + "gzip": 1022, + "brotli": 905 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.bs.js", + "minified": 1000, + "gzip": 561, + "brotli": 489 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordQuery_graphql.bs.js", + "minified": 3761, + "gzip": 1393, + "brotli": 1240 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.bs.js", + "minified": 4514, + "gzip": 1581, + "brotli": 1426 + }, + { + "path": "__tests__/__generated__/TestRefetchingRecord_user_graphql.bs.js", + "minified": 2080, + "gzip": 827, + "brotli": 721 + }, + { + "path": "__tests__/__generated__/TestRefetchingRefetchQuery_graphql.bs.js", + "minified": 4472, + "gzip": 1574, + "brotli": 1420 + }, + { + "path": "__tests__/__generated__/TestRefetching_user_graphql.bs.js", + "minified": 2068, + "gzip": 823, + "brotli": 721 + }, + { + "path": "__tests__/__generated__/TestRelayResolversAllQuery_graphql.bs.js", + "minified": 5361, + "gzip": 1536, + "brotli": 1386 + }, + { + "path": "__tests__/__generated__/TestRelayResolversQuery_graphql.bs.js", + "minified": 3046, + "gzip": 1181, + "brotli": 1052 + }, + { + "path": "__tests__/__generated__/TestRelayResolvers_user_graphql.bs.js", + "minified": 1300, + "gzip": 625, + "brotli": 542 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver2_graphql.bs.js", + "minified": 794, + "gzip": 465, + "brotli": 395 + }, + { + "path": "__tests__/__generated__/TestRelayUserResolver_graphql.bs.js", + "minified": 793, + "gzip": 463, + "brotli": 395 + }, + { + "path": "__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.bs.js", + "minified": 2191, + "gzip": 967, + "brotli": 855 + }, + { + "path": "__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.bs.js", + "minified": 1796, + "gzip": 841, + "brotli": 735 + }, + { + "path": "__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.bs.js", + "minified": 1829, + "gzip": 827, + "brotli": 719 + }, + { + "path": "__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.bs.js", + "minified": 902, + "gzip": 515, + "brotli": 443 + }, + { + "path": "__tests__/__generated__/TestSubscriptionQuery_graphql.bs.js", + "minified": 2542, + "gzip": 1073, + "brotli": 942 + }, + { + "path": "__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.bs.js", + "minified": 2552, + "gzip": 952, + "brotli": 837 + }, + { + "path": "__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.bs.js", + "minified": 3038, + "gzip": 1066, + "brotli": 953 + }, + { + "path": "__tests__/__generated__/TestSubscription_user_graphql.bs.js", + "minified": 1061, + "gzip": 535, + "brotli": 455 + }, + { + "path": "__tests__/__generated__/TestTestingHelpersQuery_graphql.bs.js", + "minified": 3448, + "gzip": 1298, + "brotli": 1170 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_plural_user_graphql.bs.js", + "minified": 1002, + "gzip": 538, + "brotli": 466 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_sub_user_graphql.bs.js", + "minified": 724, + "gzip": 458, + "brotli": 390 + }, + { + "path": "__tests__/__generated__/TestTestingHelpers_user_graphql.bs.js", + "minified": 1180, + "gzip": 607, + "brotli": 521 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentQuery_graphql.bs.js", + "minified": 3556, + "gzip": 1264, + "brotli": 1124 + }, + { + "path": "__tests__/__generated__/TestUnionFragmentUser_user_graphql.bs.js", + "minified": 923, + "gzip": 520, + "brotli": 448 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_member_graphql.bs.js", + "minified": 1547, + "gzip": 696, + "brotli": 609 + }, + { + "path": "__tests__/__generated__/TestUnionFragment_plural_member_graphql.bs.js", + "minified": 1561, + "gzip": 707, + "brotli": 634 + }, + { + "path": "__tests__/__generated__/TestUnionsQuery_graphql.bs.js", + "minified": 5037, + "gzip": 1522, + "brotli": 1380 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.bs.js", + "minified": 2816, + "gzip": 1121, + "brotli": 997 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_query_graphql.bs.js", + "minified": 1248, + "gzip": 607, + "brotli": 517 + }, + { + "path": "__tests__/__generated__/TestUpdatableFragments_updatableUser_graphql.bs.js", + "minified": 678, + "gzip": 363, + "brotli": 321 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.bs.js", + "minified": 1298, + "gzip": 610, + "brotli": 529 + }, + { + "path": "__tests__/__generated__/TestUpdatableQuery_graphql.bs.js", + "minified": 2607, + "gzip": 1046, + "brotli": 936 + }, + { + "path": "__tests__/__generated__/UserAvatar_user_graphql.bs.js", + "minified": 1049, + "gzip": 572, + "brotli": 494 + }, + { + "path": "__tests__/__generated__/UserMeta____relay_model_instance_graphql.bs.js", + "minified": 785, + "gzip": 476, + "brotli": 404 + }, + { + "path": "__tests__/__generated__/UserMeta_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests__/__generated__/UserName_user_graphql.bs.js", + "minified": 785, + "gzip": 458, + "brotli": 393 + }, + { + "path": "__tests__/__generated__/User_relayResolvers_graphql.bs.js", + "minified": 0, + "gzip": 20, + "brotli": 1 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.bs.js", + "minified": 1418, + "gzip": 570, + "brotli": 499 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.bs.js", + "minified": 5051, + "gzip": 1484, + "brotli": 1332 + }, + { + "path": "__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.bs.js", + "minified": 2236, + "gzip": 845, + "brotli": 759 + } + ] +} diff --git a/packages/rescript-relay/benchmarks/results/size-runtime.json b/packages/rescript-relay/benchmarks/results/size-runtime.json new file mode 100644 index 00000000..46b94b7f --- /dev/null +++ b/packages/rescript-relay/benchmarks/results/size-runtime.json @@ -0,0 +1,21 @@ +{ + "esbuild": "0.25.12", + "node": "v24.16.0", + "results": { + "original": { + "minified": 2916, + "gzip": 1144, + "brotli": 1010 + }, + "before": { + "minified": 5605, + "gzip": 2261, + "brotli": 2041 + }, + "current": { + "minified": 5786, + "gzip": 2330, + "brotli": 2108 + } + } +} diff --git a/packages/rescript-relay/benchmarks/size-budgets.json b/packages/rescript-relay/benchmarks/size-budgets.json new file mode 100644 index 00000000..cda6d1ca --- /dev/null +++ b/packages/rescript-relay/benchmarks/size-budgets.json @@ -0,0 +1,12 @@ +{ + "combined": { + "minified": 275365, + "gzip": 36682, + "brotli": 26495 + }, + "separate": { + "minified": 305561, + "gzip": 119565, + "brotli": 106023 + } +} diff --git a/packages/rescript-relay/benchmarks/size-fixtures.json b/packages/rescript-relay/benchmarks/size-fixtures.json new file mode 100644 index 00000000..17e4b545 --- /dev/null +++ b/packages/rescript-relay/benchmarks/size-fixtures.json @@ -0,0 +1,131 @@ +[ + "__tests__/__generated__/GroupAvatar_group_graphql.bs.js", + "__tests__/__generated__/HasNameComponent_hasName_graphql.bs.js", + "__tests__/__generated__/LocalUser____relay_model_instance_graphql.bs.js", + "__tests__/__generated__/LocalUser__id_graphql.bs.js", + "__tests__/__generated__/LocalUser_relayResolvers_graphql.bs.js", + "__tests__/__generated__/Query_relayResolvers_graphql.bs.js", + "__tests__/__generated__/RichContent_content_graphql.bs.js", + "__tests__/__generated__/TestAliasedFragmentsQuery_graphql.bs.js", + "__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.bs.js", + "__tests__/__generated__/TestAliasedFragments_userLastName_graphql.bs.js", + "__tests__/__generated__/TestArrayUnderlyingScalarSiblingQuery_graphql.bs.js", + "__tests__/__generated__/TestCatchLoggedInUserPropQuery_graphql.bs.js", + "__tests__/__generated__/TestCatchMemberPropNestedQuery_graphql.bs.js", + "__tests__/__generated__/TestCatchMemberPropQuery_graphql.bs.js", + "__tests__/__generated__/TestCatchMembersPropQuery_graphql.bs.js", + "__tests__/__generated__/TestCatchUnionMemberQuery_graphql.bs.js", + "__tests__/__generated__/TestCatchUnionMember_member_graphql.bs.js", + "__tests__/__generated__/TestCatchUser_user_graphql.bs.js", + "__tests__/__generated__/TestCodesplitQuery_graphql.bs.js", + "__tests__/__generated__/TestCompileInterfacesFragment_user_graphql.bs.js", + "__tests__/__generated__/TestConnections2_user_graphql.bs.js", + "__tests__/__generated__/TestConnections3_user_graphql.bs.js", + "__tests__/__generated__/TestConnectionsQuery_graphql.bs.js", + "__tests__/__generated__/TestConnections_AddFriendMutation_graphql.bs.js", + "__tests__/__generated__/TestConnections_user_graphql.bs.js", + "__tests__/__generated__/TestCustomScalarAfterCustomScalarArrayQuery_graphql.bs.js", + "__tests__/__generated__/TestCustomScalarsQuery_graphql.bs.js", + "__tests__/__generated__/TestFragmentQuery_graphql.bs.js", + "__tests__/__generated__/TestFragmentRequiredPlural_user_graphql.bs.js", + "__tests__/__generated__/TestFragmentRequiredQuery_graphql.bs.js", + "__tests__/__generated__/TestFragmentRequired_user_graphql.bs.js", + "__tests__/__generated__/TestFragment_allowUnsafeEnum_graphql.bs.js", + "__tests__/__generated__/TestFragment_inline_graphql.bs.js", + "__tests__/__generated__/TestFragment_plural_user_graphql.bs.js", + "__tests__/__generated__/TestFragment_sub_user_graphql.bs.js", + "__tests__/__generated__/TestFragment_user_graphql.bs.js", + "__tests__/__generated__/TestInputUnionQuery_graphql.bs.js", + "__tests__/__generated__/TestLocalPayloadQuery_graphql.bs.js", + "__tests__/__generated__/TestLocalPayloadViaNodeInterfaceQuery_graphql.bs.js", + "__tests__/__generated__/TestLocalPayload_user_graphql.bs.js", + "__tests__/__generated__/TestMissingFieldHandlersLinkedMeQuery_graphql.bs.js", + "__tests__/__generated__/TestMissingFieldHandlersLinkedQuery_graphql.bs.js", + "__tests__/__generated__/TestMissingFieldHandlersPluralBestiesQuery_graphql.bs.js", + "__tests__/__generated__/TestMissingFieldHandlersPluralQuery_graphql.bs.js", + "__tests__/__generated__/TestMissingFieldHandlersScalarMeQuery_graphql.bs.js", + "__tests__/__generated__/TestMissingFieldHandlersScalarQuery_graphql.bs.js", + "__tests__/__generated__/TestMutationInline_user_graphql.bs.js", + "__tests__/__generated__/TestMutationProvidedVariable_user_graphql.bs.js", + "__tests__/__generated__/TestMutationQuery_graphql.bs.js", + "__tests__/__generated__/TestMutationSetOnlineStatusComplexMutation_graphql.bs.js", + "__tests__/__generated__/TestMutationSetOnlineStatusMutation_graphql.bs.js", + "__tests__/__generated__/TestMutationWithInlineFragmentSetOnlineStatusMutation_graphql.bs.js", + "__tests__/__generated__/TestMutationWithOnlyFragmentSetOnlineStatusMutation_graphql.bs.js", + "__tests__/__generated__/TestMutationWithProvidedVariableFragmentMutation_graphql.bs.js", + "__tests__/__generated__/TestMutation_user_graphql.bs.js", + "__tests__/__generated__/TestNodeInterfaceOnAbstractTypeQuery_graphql.bs.js", + "__tests__/__generated__/TestNodeInterfaceQuery_graphql.bs.js", + "__tests__/__generated__/TestNodeInterface_user_graphql.bs.js", + "__tests__/__generated__/TestNonReactQuery_graphql.bs.js", + "__tests__/__generated__/TestNonReact_user_graphql.bs.js", + "__tests__/__generated__/TestNullableScalarsAfterCustomScalarArrayQuery_graphql.bs.js", + "__tests__/__generated__/TestNullableVariablesMutation_graphql.bs.js", + "__tests__/__generated__/TestNullableVariablesQuery_graphql.bs.js", + "__tests__/__generated__/TestObserverQuery_graphql.bs.js", + "__tests__/__generated__/TestPaginationInNodeQuery_graphql.bs.js", + "__tests__/__generated__/TestPaginationInNodeRefetchQuery_graphql.bs.js", + "__tests__/__generated__/TestPaginationInNode_query_graphql.bs.js", + "__tests__/__generated__/TestPaginationInNode_user_graphql.bs.js", + "__tests__/__generated__/TestPaginationUnionQuery_graphql.bs.js", + "__tests__/__generated__/TestPaginationUnionRefetchQuery_graphql.bs.js", + "__tests__/__generated__/TestPaginationUnion_query_graphql.bs.js", + "__tests__/__generated__/TestPaginationUnion_user_graphql.bs.js", + "__tests__/__generated__/TestParseCustomScalarArrayQuery_graphql.bs.js", + "__tests__/__generated__/TestPrefetchablePaginationQuery_graphql.bs.js", + "__tests__/__generated__/TestPrefetchablePaginationRefetchQuery_graphql.bs.js", + "__tests__/__generated__/TestPrefetchablePagination_user__edges_graphql.bs.js", + "__tests__/__generated__/TestPrefetchablePagination_user_graphql.bs.js", + "__tests__/__generated__/TestProvidedVariablesQuery_graphql.bs.js", + "__tests__/__generated__/TestProvidedVariables_user_graphql.bs.js", + "__tests__/__generated__/TestQueryInputObjectsQuery_graphql.bs.js", + "__tests__/__generated__/TestQuery_graphql.bs.js", + "__tests__/__generated__/TestRefetchingInNodeQuery_graphql.bs.js", + "__tests__/__generated__/TestRefetchingInNodeRefetchQuery_graphql.bs.js", + "__tests__/__generated__/TestRefetchingInNode_user_graphql.bs.js", + "__tests__/__generated__/TestRefetchingNoArgsRefetchQuery_graphql.bs.js", + "__tests__/__generated__/TestRefetchingNoArgs_query_graphql.bs.js", + "__tests__/__generated__/TestRefetchingQuery_graphql.bs.js", + "__tests__/__generated__/TestRefetchingRecordNoArgsRefetchQuery_graphql.bs.js", + "__tests__/__generated__/TestRefetchingRecordNoArgs_query_graphql.bs.js", + "__tests__/__generated__/TestRefetchingRecordQuery_graphql.bs.js", + "__tests__/__generated__/TestRefetchingRecordRefetchQuery_graphql.bs.js", + "__tests__/__generated__/TestRefetchingRecord_user_graphql.bs.js", + "__tests__/__generated__/TestRefetchingRefetchQuery_graphql.bs.js", + "__tests__/__generated__/TestRefetching_user_graphql.bs.js", + "__tests__/__generated__/TestRelayResolversAllQuery_graphql.bs.js", + "__tests__/__generated__/TestRelayResolversQuery_graphql.bs.js", + "__tests__/__generated__/TestRelayResolvers_user_graphql.bs.js", + "__tests__/__generated__/TestRelayUserResolver2_graphql.bs.js", + "__tests__/__generated__/TestRelayUserResolver_graphql.bs.js", + "__tests__/__generated__/TestRequiredFieldLoggerQuery_graphql.bs.js", + "__tests__/__generated__/TestSerializeCustomScalarArrayMutation_graphql.bs.js", + "__tests__/__generated__/TestSerializeMultipleCustomScalarsMutation_graphql.bs.js", + "__tests__/__generated__/TestSubscriptionProvidedVariable_user_graphql.bs.js", + "__tests__/__generated__/TestSubscriptionQuery_graphql.bs.js", + "__tests__/__generated__/TestSubscriptionUserUpdatedSubscription_graphql.bs.js", + "__tests__/__generated__/TestSubscriptionWithProvidedVariableFragmentSubscription_graphql.bs.js", + "__tests__/__generated__/TestSubscription_user_graphql.bs.js", + "__tests__/__generated__/TestTestingHelpersQuery_graphql.bs.js", + "__tests__/__generated__/TestTestingHelpers_plural_user_graphql.bs.js", + "__tests__/__generated__/TestTestingHelpers_sub_user_graphql.bs.js", + "__tests__/__generated__/TestTestingHelpers_user_graphql.bs.js", + "__tests__/__generated__/TestUnionFragmentQuery_graphql.bs.js", + "__tests__/__generated__/TestUnionFragmentUser_user_graphql.bs.js", + "__tests__/__generated__/TestUnionFragment_member_graphql.bs.js", + "__tests__/__generated__/TestUnionFragment_plural_member_graphql.bs.js", + "__tests__/__generated__/TestUnionsQuery_graphql.bs.js", + "__tests__/__generated__/TestUpdatableFragmentsQuery_graphql.bs.js", + "__tests__/__generated__/TestUpdatableFragments_query_graphql.bs.js", + "__tests__/__generated__/TestUpdatableFragments_updatableUser_graphql.bs.js", + "__tests__/__generated__/TestUpdatableQuery_UpdatableQuery_graphql.bs.js", + "__tests__/__generated__/TestUpdatableQuery_graphql.bs.js", + "__tests__/__generated__/UserAvatar_user_graphql.bs.js", + "__tests__/__generated__/UserMeta____relay_model_instance_graphql.bs.js", + "__tests__/__generated__/UserMeta_relayResolvers_graphql.bs.js", + "__tests__/__generated__/UserName_user_graphql.bs.js", + "__tests__/__generated__/User_relayResolvers_graphql.bs.js", + "__tests_preloaded__/__generated__/TestPreloadedQueryProvidedVariables_user_graphql.bs.js", + "__tests_preloaded__/__generated__/TestPreloadedQuery_graphql.bs.js", + "__tests_preloaded__/__generated__/TestPreloadedQuery_preloadable_graphql.bs.js" +] diff --git a/packages/rescript-relay/docs/conversion-contract.md b/packages/rescript-relay/docs/conversion-contract.md new file mode 100644 index 00000000..415d84f8 --- /dev/null +++ b/packages/rescript-relay/docs/conversion-contract.md @@ -0,0 +1,161 @@ +# Conversion contract and audit + +Coverage is not proof of correctness. This document distinguishes the intended +contract from historical behavior and limits of the input protocol. Tests must +assert the contract independently of the optimized implementation. + +## Sources and scope + +The audited compiler is submodule `ef6d68326a6661887a2a09fd12028f201e32d78f`: +`relay-typegen/src/rescript_ast.rs` (`ConverterInstructions`), `rescript.rs` +(`ast_to_prop_value`, `write_internal_assets`), and `rescript_utils.rs` +(`instruction_to_key_value_pair`). All checked-in generated artifacts were also +inspected. Legacy compiler opcodes are `c`, `ca`, `r`, `u`, `f`, and `b`; +`e` is retained for older artifacts. Nullable conversion happens implicitly, +regardless of historical `n`/`na` hints. Response/fragment conversion, variables, +provided variables, mutation/raw responses, and persisted queries use this API. + +[PR #673](https://github.com/zth/rescript-relay/pull/673) independently replaces +path arrays with strings. Its regression cases are retained here, and its +implementation is included in the benchmark comparisons. Its reported real-app +measurements are separate evidence, not our local microbenchmark results. + +## Supported data + +- Plain, own enumerable string-keyed data records and arrays, including sparse + arrays, null-prototype records, frozen inputs, and shared subtrees. +- Null and undefined normalize to the requested nullable sentinel. Missing + properties remain missing; false, zero, empty strings, and option markers + remain distinct. Converter outputs are opaque even when they are null, + undefined, arrays, class instances, or objects containing nulls. +- Relay metadata beginning `__` is opaque, except `__id`, `__typename`, and + `__relay_internal*` provided variables. Cycles in opaque metadata/scalars/JSON + are allowed. Cycles in traversed GraphQL data, accessors, proxies, symbol-keyed + selections, and invalid primitive roots are outside the GraphQL contract. +- No input, converter-produced object, instruction, or Relay store record may + be mutated. Unchanged branches retain identity. Aliases may share input + objects but conversions are path-specific, not memoized by object identity. +- Converter exceptions propagate unchanged. Siblings are processed in own + property enumeration order and array elements in index order, once each. + No callback-result or snapshot cache is permitted. + +## Intended operations + +| Operation | Semantics | +|---|---| +| Record | Visit fields once, preserving absent fields and opaque metadata. | +| List | Preserve holes; normalize null elements; apply the element plan once. | +| Scalar | Skip absent values/options; invoke callback once; never traverse its result. | +| Opaque JSON | Normalize the field's absence; preserve the entire present value by identity. | +| Fragment refs | Add two properties pointing at the same shallow raw snapshot; never recursively convert this generated snapshot. | +| Union | Select by the original `__typename`; on reads convert members before the union callback; on writes invoke the callback before member conversion. Root and nested unions obey the same order. Unknown types use the generated union callback. | +| Input reference | Unwrap `__$inputUnion` to its selected field, then use the named root's plan. Recursion follows data, not plan construction. | + +A list containing array-valued custom scalars is distinct from a nested GraphQL +list. The protocol must preserve that distinction, not infer it from runtime +array shape. Null elements never reach a custom scalar's parse/serialize function; +they belong to the GraphQL nullable wrapper, not the scalar implementation. + +## Problems found beyond coverage + +1. Two existing tests explicitly blessed `parse(null)` / `serialize(null)` in + scalar lists as "CURRENT BEHAVIOR". They characterized a bug, not the contract. +2. Joining paths with `_` aliases e.g. field `a_b` and nested fields `a.b`. + String paths avoid allocations but cannot resolve this ambiguity. +3. `found_in_array: bool` loses nested list depth. The old `ca`/`b:a` protocol + cannot distinguish nested GraphQL lists from array-valued scalars/JSON. +4. Root union callbacks historically run after traversal in both directions, + unlike nested unions. Identity write callbacks hid the ordering mismatch. +5. Previous fixes addressed repeated traversal of recursive arrays, traversal of + generated fragment snapshots, and one union element affecting its siblings. + +## Redesign boundary + +New artifacts use a versioned, lossless plan with path segments and explicit +list depth. Plans are prepared once at module initialization into reusable +converters; response conversion does not construct paths or decode opcodes. +Prepared converters snapshot their plan and callback bindings; mutating those +objects afterward is unsupported and must not silently change a prepared plan. +Legacy `traverser` remains available for already-generated artifacts, with its +ambiguous-path/list-depth limitations stated explicitly. New plans reject missing +callbacks/references and conflicting instructions during preparation. + +## Validation layers + +- Explicit shape, ordering, callback count, identity, exception, and nullability + tables, including field-order and instruction-order permutations. +- A simple independent reference evaluator and deterministic generated plans/data; + compare both outputs and callback traces, including round trips where invertible. +- Compiler tests for lossless paths and list depth, plus actual regenerated + artifacts compiled through the PPX/ReScript toolchain. +- Mounted query/fragment/plural/store-update tests, existing mutation/subscription/ + pagination/resolver/catch/updatable tests, and separate persisted-query tests. +- Repeatable targeted mutation tests: each injected fault must cause a behavioral + failure. Coverage thresholds are an additional gate, not the correctness oracle. +- Balanced benchmark comparisons against master, #673, and the first optimized + implementation. Measure preparation separately from warm response conversion. + +No finite suite proves all possible JavaScript behavior. Confidence depends on +this explicit supported contract, independent oracles, real integration tests, +and documented remaining limitations—not a claim of absolute certainty. + +## Compiler and package verification + +The compiler changes are in [draft zth/relay#40](https://github.com/zth/relay/pull/40), +pinned by this branch's submodule revision. All 35 compiler library tests pass. +The broader upstream Flow snapshot suite has 64 failures and 93 passes on both +the unchanged pinned compiler and this branch, with identical failing test names. +These existing failures remain outside this conversion change. + +The runtime is tested through both CommonJS and native ESM release modules. +Run `yarn test:all` and `yarn test:conversion:mutations` from the package. The +mutation check runs isolated copies and requires behavioral test failures for +20 deliberately introduced faults; it does not claim a comprehensive mutation score. + +## Generated size and sharing + +Generated helper metadata and handles are private to each artifact. Equal plans +can be shared between directions, but callback maps and prepared handles remain +separate. Empty plans reuse the runtime's callback-free nullable converter, which +retains neither inputs nor application callbacks. The named version 2 plan format +and response semantics are unchanged. See [size measurements](../benchmarks/SIZE.md) +for complete generated-artifact bundles, separate gzip/Brotli totals, and CI budgets. + +Compiler-generated callback keys may be short string IDs such as `"0"`. They are +local to a conversion's callback table, assigned deterministically across scalar +and union callbacks, and do not change response traversal. Public preparation +still accepts named callback keys. Plans may be shared, but callback bindings are +always prepared against their own table and snapshotted independently. + +The compiler may omit list-depth hints when no special conversion operation +exists at or below that path. Generic nullable traversal handles those plain +lists. List wrappers around scalars, unions, references, opaque values, and +fragment-bearing records are retained. An entirely empty plan uses the shared +converter; named input references are never removed by this optimization. + +Plural fragment roots apply their record plan to each element without a redundant +root list instruction. For plural fragments with `@catch`, each element is a +`CatchResult`, so the generated type is `array>` and +custom scalar paths start inside each result's `value`. Mounted hook tests cover +nullable payloads, union/interface payloads, mixed success/error results, store +updates, and preservation +of the raw Relay store; a typed ReScript helper checks the generated wrapper. + +## Custom scalar ordering follow-up + +`utils-scalar-ordering-tests.js` enumerates all 120 orders of five sibling fields +(single array-backed scalar, scalar list, referenced input, nullable field, and +another scalar), with forward/reversed instruction order, both nullable +directions, and both legacy/prepared runtimes. Each of the 960 combinations runs +twice against frozen input, checking exact callback order/count, result identity, +source preservation, sparse lists, and continued sibling conversion. + +Additional cases distinguish empty array-backed scalars from empty GraphQL +lists; preserve falsey/null/undefined/object/array/Date/option callback results at +root, field, and list positions; and verify that a failing list callback stops +later elements/siblings without poisoning a subsequent call. Mounted compiled +queries select the same mixed scalar/list fields in opposite orders and verify +read conversion, recursive variable serialization, write conversion, nullable +store updates, and raw-store preservation. Targeted mutations explicitly restore +the historical sibling early return and incorrectly normalize scalar results; +both must fail behavioral assertions. diff --git a/packages/rescript-relay/jest.conversion.config.js b/packages/rescript-relay/jest.conversion.config.js new file mode 100644 index 00000000..078512dc --- /dev/null +++ b/packages/rescript-relay/jest.conversion.config.js @@ -0,0 +1,24 @@ +const config = require("./jest.config"); +module.exports = { + ...config, + bail: false, + testRegex: "/__tests__/utils.*-tests.js$", + roots: ["/__tests__"], + collectCoverage: true, + collectCoverageFrom: ["src/utils.js", "src/prepareConversion.js"], + coverageDirectory: "/coverage/conversion", + coverageThreshold: { + "./src/prepareConversion.js": { + branches: 95, + functions: 100, + lines: 100, + statements: 98, + }, + "./src/utils.js": { + branches: 95, + functions: 100, + lines: 100, + statements: 98, + }, + }, +}; diff --git a/packages/rescript-relay/package.json b/packages/rescript-relay/package.json index ac5a4a8c..2427cb26 100644 --- a/packages/rescript-relay/package.json +++ b/packages/rescript-relay/package.json @@ -36,34 +36,41 @@ "build:test": "./build-compiler-dev.sh && ./rescript-relay-compiler && ENABLE_PERSISTING=true ./rescript-relay-compiler && rm ./__tests_preloaded__/__generated__/RelaySchemaAssets_graphql.res", "postinstall": "node postinstall.js", "test": "jest", - "test:ci": "jest --ci --runInBand" + "test:ci": "jest --ci --runInBand", + "test:conversion": "jest --config jest.conversion.config.js --runInBand", + "test:all": "yarn build && yarn test:ci && ENABLE_PERSISTING=true yarn test:ci && yarn test:conversion", + "bench:conversion": "node benchmarks/conversion.js", + "test:conversion:mutations": "node scripts/test-conversion-mutations.js", + "bench:conversion:prepared": "node benchmarks/conversion.js --prepared", + "size:conversion": "node scripts/measure-conversion-size.js" }, "devDependencies": { + "@babel/core": "^7.24.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/preset-env": "^7.24.0", "@glennsl/rescript-fetch": "^0.2.0", "@rescript/react": "0.14.0", "@testing-library/jest-dom": "^5.16.3", "@testing-library/react": "^13.0.0-alpha.6", - "graphql-query-test-mock": "^0.12.1", - "jest": "^27.2.4", - "@babel/core": "^7.24.0", - "@babel/preset-env": "^7.24.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", "babel-jest": "^27.2.4", "babel-plugin-dynamic-import-node": "^2.3.3", + "esbuild": "0.25.12", + "graphql-query-test-mock": "^0.12.1", + "jest": "^27.2.4", "nock": "^11.7.0", "node-fetch": "^2.6.0", "react": "18.2.0", "react-dom": "18.2.0", "react-relay": "20.1.1", - "relay-test-utils": "20.1.1", "relay-runtime": "20.1.1", + "relay-test-utils": "20.1.1", "rescript": "12.3.0" }, "peerDependencies": { "@rescript/react": ">=0.13.0", "react-relay": "20.1.1", - "relay-test-utils": "20.1.1", "relay-runtime": "20.1.1", + "relay-test-utils": "20.1.1", "rescript": "^12.0.0-0" }, "peerDependenciesMeta": { diff --git a/packages/rescript-relay/relay.config.js b/packages/rescript-relay/relay.config.js index 7a83fd75..854df400 100644 --- a/packages/rescript-relay/relay.config.js +++ b/packages/rescript-relay/relay.config.js @@ -13,6 +13,7 @@ module.exports = { IntString: "TestsUtils.IntString", JSON: "JSON.t", Number: "TestsUtils.Number", + ConversionNumber: "TestsUtils.ConversionNumber", ObjectScalar1: "TestsUtils.ObjectScalar1", ObjectScalar2: "TestsUtils.ObjectScalar2", }, diff --git a/packages/rescript-relay/scripts/build-conversion-modules.js b/packages/rescript-relay/scripts/build-conversion-modules.js new file mode 100644 index 00000000..8bbe02ba --- /dev/null +++ b/packages/rescript-relay/scripts/build-conversion-modules.js @@ -0,0 +1,35 @@ +#!/usr/bin/env node +// Keep CommonJS and ESM implementations identical. Fail if the source module +// boundary changes instead of producing a silently broken release artifact. +const fs = require("fs"); +const path = require("path"); +function buildConversionModules(destination) { + fs.mkdirSync(destination, { recursive: true }); + for (const name of ["utils", "prepareConversion"]) { + let source = fs.readFileSync( + path.join(__dirname, "../src", name + ".js"), + "utf8", + ); + const exports = /module\.exports = \{([\w\s,]+)\};\s*$/; + if (!exports.test(source)) + throw new Error("Unsupported conversion exports in " + name); + source = source.replace(exports, "export {$1};\n"); + if (name === "utils") { + const dependency = + /const\s*\{\s*prepareConversion,\s*convertWithoutPlan,?\s*\}\s*=\s*require\("\.\/prepareConversion"\);/; + if (!dependency.test(source)) + throw new Error("Missing prepared-converter import"); + source = source.replace( + dependency, + 'import { prepareConversion, convertWithoutPlan } from "./prepareConversion.mjs";', + ); + } + fs.writeFileSync(path.join(destination, name + ".mjs"), source); + } +} +if (require.main === module) { + if (!process.argv[2]) + throw new Error("Usage: build-conversion-modules.js "); + buildConversionModules(path.resolve(process.argv[2])); +} +module.exports = { buildConversionModules }; diff --git a/packages/rescript-relay/scripts/measure-conversion-size.js b/packages/rescript-relay/scripts/measure-conversion-size.js new file mode 100644 index 00000000..86d95f5d --- /dev/null +++ b/packages/rescript-relay/scripts/measure-conversion-size.js @@ -0,0 +1,133 @@ +#!/usr/bin/env node +// Measure complete compiled artifacts, not just instruction literals. Dependencies +// are external so application/runtime size is not mistaken for generated-code size. +const fs = require("fs"); +const path = require("path"); +const zlib = require("zlib"); +const esbuild = require("esbuild"); +const args = process.argv.slice(2); +const option = (name) => { + const index = args.indexOf(name); + return index < 0 ? undefined : args[index + 1]; +}; +const root = path.resolve(option("--root") || path.join(__dirname, "..")); +const manifest = JSON.parse( + fs.readFileSync( + path.join(__dirname, "../benchmarks/size-fixtures.json"), + "utf8", + ), +); +function size(bytes) { + return { + minified: bytes.length, + gzip: zlib.gzipSync(bytes, { level: 9 }).length, + brotli: zlib.brotliCompressSync(bytes, { + params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 }, + }).length, + }; +} +async function measure() { + const common = { + bundle: true, + minify: true, + format: "esm", + platform: "browser", + write: false, + logLevel: "silent", + plugins: [ + { + name: "external-dependencies", + setup(build) { + build.onResolve({ filter: /.*/ }, (args) => { + if (args.kind === "entry-point") return; + return { path: args.path, external: true }; + }); + }, + }, + ], + }; + const files = []; + for (const relative of manifest) { + const result = await esbuild.build({ + ...common, + entryPoints: [path.join(root, relative)], + }); + files.push({ path: relative, ...size(result.outputFiles[0].contents) }); + } + // A genuine combined bundle of all fixture modules, with dependencies external. + const combined = await esbuild.build({ + ...common, + stdin: { + contents: manifest + .map( + (p, i) => + `export * as artifact${i} from ${JSON.stringify(path.join(root, p))};`, + ) + .join("\n"), + resolveDir: root, + }, + plugins: [ + { + name: "external-dependencies", + setup(build) { + build.onResolve({ filter: /.*/ }, (args) => { + if (args.importer === "") return { path: args.path }; + return { path: args.path, external: true }; + }); + }, + }, + ], + }); + const separate = files.reduce( + (sum, file) => + Object.fromEntries( + Object.keys(sum).map((key) => [key, sum[key] + file[key]]), + ), + { minified: 0, gzip: 0, brotli: 0 }, + ); + const report = { + fixtureVersion: 1, + esbuild: esbuild.version, + node: process.version, + artifactCount: files.length, + combined: size(combined.outputFiles[0].contents), + separate, + files, + }; + console.log( + JSON.stringify( + { + artifactCount: report.artifactCount, + combined: report.combined, + separate, + }, + null, + 2, + ), + ); + if (option("--output")) + fs.writeFileSync( + option("--output"), + JSON.stringify(report, null, 2) + "\n", + ); + if (args.includes("--check")) { + // Intentional headroom. Changing the fixture manifest or minifier requires + // reviewing these budgets; these do not claim to bound an application's size. + const budgets = JSON.parse( + fs.readFileSync( + path.join(__dirname, "../benchmarks/size-budgets.json"), + "utf8", + ), + ); + for (const mode of ["combined", "separate"]) + for (const key of ["minified", "gzip", "brotli"]) + if (report[mode][key] > budgets[mode][key]) + throw new Error( + `${mode} ${key}: ${report[mode][key]} exceeds ${budgets[mode][key]}`, + ); + } +} +measure().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/packages/rescript-relay/scripts/test-conversion-mutations.js b/packages/rescript-relay/scripts/test-conversion-mutations.js new file mode 100644 index 00000000..268417ce --- /dev/null +++ b/packages/rescript-relay/scripts/test-conversion-mutations.js @@ -0,0 +1,195 @@ +#!/usr/bin/env node +// Targeted behavioral mutation checks, not an exhaustive mutation score. +// Run in isolated temporary modules; never rewrite the working source. +const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const { spawn } = require("child_process"); +const root = path.resolve(__dirname, ".."); +const source = fs.readFileSync( + path.join(root, "src/prepareConversion.js"), + "utf8", +); +const directory = fs.mkdtempSync( + path.join(os.tmpdir(), "relay-conversion-mutations-"), +); +const config = { + ...require("../jest.config"), + rootDir: root, + bail: false, + collectCoverage: false, + roots: [path.join(root, "__tests__")], + testRegex: "/utils-(prepared|scalar-ordering)-tests.js$", +}; +const faults = [ + [ + "null handling", + "value == null ? nullable : isOption(value)", + "value == null ? value : isOption(value)", + ], + [ + "option markers", + "isOption(value) ? value : convert(value)", + "isOption(value) ? convert(value) : convert(value)", + ], + ["opaque sparse lists", "if (!(i in values)) continue;", ""], + [ + "callback sparse lists", + "for (let i = 0; i < values.length; i++) {\n if (!(i in values)) continue;", + "for (let i = 0; i < values.length; i++) {", + ], + [ + "opaque-list null handling", + "const next = value == null ? nullable : value;", + "const next = value;", + ], + [ + "opaque-list boundary", + "const next = value == null ? nullable : value;", + "const next = any(value);", + ], + [ + "generic object mutation", + "const next = any(original);\n if (next !== original) {\n if (result === undefined) result = { ...value };", + "const next = any(original);\n if (next !== original) {\n if (result === undefined) result = value;", + ], + [ + "generic metadata boundary", + "function plainObject(value) {\n let result;\n for (const key in value) {\n if (!hasOwn(value, key) || isMetadata(key)) continue;", + "function plainObject(value) {\n let result;\n for (const key in value) {\n if (!hasOwn(value, key)) continue;", + ], + [ + "callback count", + "const next = convert(value);", + "const next = convert(convert(value));", + ], + [ + "source mutation", + "if (result === undefined) result = { ...value };", + "if (result === undefined) result = value;", + ], + [ + "shared fragment refs", + "updatableFragmentRefs: refs", + "updatableFragmentRefs: { ...refs }", + ], + [ + "scalar boundary", + "convert = callback(node.scalar);", + "convert = value => any(callback(node.scalar)(value));", + ], + [ + "scalar result nullability", + "convert = callback(node.scalar);", + "convert = value => callback(node.scalar)(value) ?? nullable;", + ], + [ + "early return after converted sibling", + "result[key] = next;\n }\n }", + "result[key] = next;\n }\n if (convert !== undefined) return result === undefined ? value : result;\n }", + ], + [ + "union ordering", + "(children[value.__typename] || any)(union(value))", + "union((children[value.__typename] || any)(value))", + ], + ["union dispatch", "children[value.__typename]", "children.User"], + ["list depth", "i < depth;", "i < Math.min(depth, 1);"], + [ + "path boundaries", + "for (const key of entry.path)", + 'for (const key of [entry.path.join("_")])', + ], + [ + "callback snapshot", + "return callbacks[name];", + "return value => callbacks[name](value);", + ], + [ + "opaque metadata", + "!hasOwn(value, key) || isMetadata(key)", + "!hasOwn(value, key)", + ], +]; +function run(configuration) { + const file = path.join(directory, "jest.json"); + fs.writeFileSync(file, JSON.stringify(configuration)); + const report = path.join(directory, "results.json"); + const log = path.join(directory, "jest.log"); + fs.rmSync(report, { force: true }); + const fd = fs.openSync(log, "w"); + return new Promise((resolve, reject) => { + const child = spawn( + process.execPath, + [ + require.resolve("jest/bin/jest"), + "--config", + file, + "--runInBand", + "--no-cache", + "--json", + "--outputFile", + report, + ], + { + cwd: root, + stdio: ["ignore", fd, fd], + env: { ...process.env, FORCE_COLOR: "0", CI: "true" }, + }, + ); + fs.closeSync(fd); + const timer = setTimeout(() => child.kill("SIGKILL"), 30000); + child.on("error", (error) => { + clearTimeout(timer); + reject(error); + }); + child.on("close", (status, signal) => { + clearTimeout(timer); + const output = fs.readFileSync(log, "utf8"); + if (signal || !fs.existsSync(report)) + return reject( + new Error("Jest did not complete: " + signal + "\n" + output), + ); + resolve({ + status, + output, + report: JSON.parse(fs.readFileSync(report, "utf8")), + }); + }); + }); +} +async function main() { + try { + const control = await run(config); + if (control.status !== 0 || control.report.numPassedTests === 0) + throw new Error("Unmodified control failed:\n" + control.output); + for (const [name, before, after] of faults) { + if (!source.includes(before)) + throw new Error("Mutation no longer matches source: " + name); + const mutant = path.join(directory, "prepareConversion.js"); + fs.writeFileSync(mutant, source.replace(before, after)); + const result = await run({ + ...config, + moduleNameMapper: { "^\\./prepareConversion$": mutant }, + }); + if (result.status === 0 || result.report.numFailedTests === 0) { + throw new Error( + "Mutation survived or failed without a behavioral assertion: " + + name + + "\n" + + result.output, + ); + } + process.stdout.write("Detected: " + name + "\n"); + } + process.stdout.write( + faults.length + " / " + faults.length + " targeted mutations detected.\n", + ); + } finally { + fs.rmSync(directory, { recursive: true, force: true }); + } +} +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/packages/rescript-relay/src/RescriptRelay.res b/packages/rescript-relay/src/RescriptRelay.res index 67265b80..c19fbd0c 100644 --- a/packages/rescript-relay/src/RescriptRelay.res +++ b/packages/rescript-relay/src/RescriptRelay.res @@ -112,6 +112,19 @@ external relayFeatureFlags: featureFlags = "RelayFeatureFlags" @module("./utils") external convertObj: ('a, dict>>, 'b, 'c) => 'd = "traverser" +/**Internal: prepare the compiler's versioned conversion plan once.*/ +@module("./utils") +external convertWithoutPlan: ('input, 'nullable) => 'output = "convertWithoutPlan" + +type preparedConversion + +@module("./utils") +external prepareConversion: (JSON.t, 'converters, 'nullable) => preparedConversion = "prepareConversion" + +@module("./utils") +external runConversion: (preparedConversion, 'input) => 'output = "runConversion" + + let optArrayOfNullableToOptArrayOfOpt: option>> => option< array>, > = x => diff --git a/packages/rescript-relay/src/RescriptRelay.resi b/packages/rescript-relay/src/RescriptRelay.resi index b36eff1f..bb71e316 100644 --- a/packages/rescript-relay/src/RescriptRelay.resi +++ b/packages/rescript-relay/src/RescriptRelay.resi @@ -210,6 +210,19 @@ external storeRootType: string = "ROOT_TYPE" @module("./utils") external convertObj: ('a, dict>>, 'b, 'c) => 'd = "traverser" +/**Internal: prepare the compiler's versioned conversion plan once.*/ +@module("./utils") +external convertWithoutPlan: ('input, 'nullable) => 'output = "convertWithoutPlan" + +type preparedConversion + +@module("./utils") +external prepareConversion: (JSON.t, 'converters, 'nullable) => preparedConversion = "prepareConversion" + +@module("./utils") +external runConversion: (preparedConversion, 'input) => 'output = "runConversion" + + /**Read the following section on working with the Relay store: https://relay.dev/docs/en/relay-store*/ module RecordProxy: { /**Read the following section on working with the Relay store: https://relay.dev/docs/en/relay-store*/ diff --git a/packages/rescript-relay/src/prepareConversion.js b/packages/rescript-relay/src/prepareConversion.js new file mode 100644 index 00000000..6448952e --- /dev/null +++ b/packages/rescript-relay/src/prepareConversion.js @@ -0,0 +1,248 @@ +const hasOwn = (object, key) => + Object.prototype.hasOwnProperty.call(object, key); +const isOption = (value) => value.BS_PRIVATE_NESTED_SOME_NONE >= 0; +const isMetadata = (key) => + key.startsWith("__") && + key !== "__typename" && + key !== "__id" && + !key.startsWith("__relay_internal"); + +// Preparation is deliberately separate from response traversal. It validates and +// snapshots compiler metadata once; the returned functions only visit values. +function readPlan(plan) { + if ( + !plan || + plan.version !== 2 || + !plan.roots || + typeof plan.roots !== "object" + ) { + throw new Error("Invalid conversion plan: expected version 2 and roots"); + } + const roots = Object.create(null); + function node() { + return { children: Object.create(null) }; + } + const keys = new Set([ + "path", + "list", + "scalar", + "union", + "reference", + "opaque", + "fragments", + ]); + for (const name of Object.keys(plan.roots)) { + const entries = plan.roots[name]; + if (!Array.isArray(entries)) + throw new Error("Invalid conversion root: " + name); + const root = (roots[name] = node()); + for (const entry of entries) { + if ( + !entry || + !Array.isArray(entry.path) || + !entry.path.every((key) => typeof key === "string") + ) { + throw new Error("Invalid conversion path in " + name); + } + let target = root; + for (const key of entry.path) + target = target.children[key] || (target.children[key] = node()); + for (const key of Object.keys(entry)) { + if (!keys.has(key)) + throw new Error("Unknown conversion instruction: " + key); + if (key === "path") continue; + if (hasOwn(target, key) && target[key] !== entry[key]) + throw new Error("Conflicting conversion instruction: " + key); + target[key] = entry[key]; + } + } + } + return roots; +} + +function prepareConversion(plan, callbacks, nullable, rootName = "__root") { + if (nullable !== undefined && nullable !== null) + throw new Error("Invalid conversion nullable sentinel"); + const roots = readPlan(plan); + if (!hasOwn(roots, rootName)) + throw new Error("Missing conversion root: " + rootName); + const converters = Object.create(null); + + const nullableOnly = (value) => (value == null ? nullable : value); + function optional(convert) { + if (convert === nullableOnly) return nullableOnly; + return (value) => + value == null ? nullable : isOption(value) ? value : convert(value); + } + // Opaque list elements only normalize nullability; no callback dispatch or + // traversal is needed, even when an element is itself an object or array. + function nullableArray(values) { + let result; + for (let i = 0; i < values.length; i++) { + if (!(i in values)) continue; + const value = values[i]; + const next = value == null ? nullable : value; + if (next !== value) { + if (result === undefined) result = values.slice(); + result[i] = next; + } + } + return result === undefined ? values : result; + } + function arrayOf(convert) { + return (values) => { + let result; + for (let i = 0; i < values.length; i++) { + if (!(i in values)) continue; + const value = values[i]; + const next = convert(value); + if (next !== value) { + if (result === undefined) result = values.slice(); + result[i] = next; + } + } + return result === undefined ? values : result; + }; + } + const anyArray = arrayOf((value) => any(value)); + function any(value) { + if (value == null) return nullable; + if (typeof value !== "object" || isOption(value)) return value; + return Array.isArray(value) ? anyArray(value) : plainObject(value); + } + function record(fields, fragments) { + return (value) => { + let result; + if (fragments) { + const refs = { ...value }; + result = { ...value, fragmentRefs: refs, updatableFragmentRefs: refs }; + } + for (const key in value) { + if (!hasOwn(value, key) || isMetadata(key)) continue; + const original = value[key]; + const convert = fields[key]; + const next = convert === undefined ? any(original) : convert(original); + if (next !== original) { + if (result === undefined) result = { ...value }; + result[key] = next; + } + } + return result === undefined ? value : result; + }; + } + // Generic objects have no field instructions. Avoid a dictionary lookup for + // every selected field while retaining property order and lazy copying. + function plainObject(value) { + let result; + for (const key in value) { + if (!hasOwn(value, key) || isMetadata(key)) continue; + const original = value[key]; + const next = any(original); + if (next !== original) { + if (result === undefined) result = { ...value }; + result[key] = next; + } + } + return result === undefined ? value : result; + } + function callback(name) { + if ( + !callbacks || + !hasOwn(callbacks, name) || + typeof callbacks[name] !== "function" + ) { + throw new Error("Missing conversion callback: " + name); + } + return callbacks[name]; + } + function compile(node) { + const operations = ["scalar", "union", "reference", "opaque"].filter( + (key) => hasOwn(node, key), + ); + if (operations.length > 1) + throw new Error( + "Conflicting conversion operations: " + operations.join(", "), + ); + for (const key of ["scalar", "union", "reference"]) { + if (hasOwn(node, key) && typeof node[key] !== "string") + throw new Error("Invalid conversion " + key); + } + for (const key of ["opaque", "fragments"]) { + if (hasOwn(node, key) && node[key] !== true) + throw new Error("Invalid conversion " + key); + } + const depth = node.list === undefined ? 0 : node.list; + if (!Number.isSafeInteger(depth) || depth < 0) + throw new Error("Invalid conversion list depth"); + const names = Object.keys(node.children); + let convert; + if ( + node.scalar !== undefined || + node.opaque || + node.reference !== undefined + ) { + if (names.length || node.fragments) + throw new Error( + "Leaf conversion cannot have child fields or fragments", + ); + if (node.scalar !== undefined) convert = callback(node.scalar); + else if (node.opaque) convert = nullableOnly; + else { + const target = node.reference; + if (!hasOwn(roots, target)) + throw new Error("Missing conversion reference: " + target); + convert = (value) => + converters[target]( + hasOwn(value, "__$inputUnion") + ? { [value.__$inputUnion]: value._0 } + : value, + ); + } + } else { + const children = Object.create(null); + for (const name of names) children[name] = compile(node.children[name]); + if (node.union !== undefined) { + if (node.fragments) + throw new Error("Union fragments belong on member plans"); + const union = callback(node.union); + convert = + nullable === null + ? (value) => (children[value.__typename] || any)(union(value)) + : (value) => union((children[value.__typename] || any)(value)); + } else { + convert = + names.length === 0 && !node.fragments + ? any + : record(children, node.fragments === true); + } + } + convert = optional(convert); + for (let i = 0; i < depth; i++) { + convert = optional(convert === nullableOnly ? nullableArray : arrayOf(convert)); + } + return convert; + } + for (const name of Object.keys(roots)) + converters[name] = compile(roots[name]); + const convert = converters[rootName]; + // Plural fragments share their record/union plan with singular fragments. + const plural = arrayOf(convert); + const root = roots[rootName]; + const allowPlural = + root.list === undefined && + root.scalar === undefined && + root.reference === undefined && + !root.opaque; + return (value) => + Array.isArray(value) && allowPlural ? plural(value) : convert(value); +} + +// Shared converters contain no application callbacks or response cache. +const emptyPlan = { version: 2, roots: { __root: [] } }; +const readWithoutPlan = prepareConversion(emptyPlan, undefined, undefined); +const writeWithoutPlan = prepareConversion(emptyPlan, undefined, null); +function convertWithoutPlan(value, nullable) { + return nullable === null ? writeWithoutPlan(value) : readWithoutPlan(value); +} + +module.exports = { prepareConversion, convertWithoutPlan }; diff --git a/packages/rescript-relay/src/utils.js b/packages/rescript-relay/src/utils.js index f1b6c687..065dd1e0 100644 --- a/packages/rescript-relay/src/utils.js +++ b/packages/rescript-relay/src/utils.js @@ -1,61 +1,38 @@ -function getNewObj(maybeNewObj, currentObj) { - return maybeNewObj || Object.assign({}, currentObj); -} - -function getPathName(path) { - return path.join("_"); -} - -function makeNewPath(currentPath, newKeys) { - return [].concat(currentPath, newKeys); -} +const { + prepareConversion, + convertWithoutPlan, +} = require("./prepareConversion"); -function getTypename(v) { - if (v != null && typeof v === "object" && v.__typename != null) { - return v.__typename; - } -} +// Shared read-only fallback: avoid allocating an empty map for every field. +var empty = Object.freeze({}); function unwrapInputUnion(obj) { if (obj != null && typeof obj === "object" && "__$inputUnion" in obj) { - return { - [obj["__$inputUnion"]]: obj["_0"], - }; + return { [obj["__$inputUnion"]]: obj._0 }; } - return obj; } -/** - * Runs on each object in the tree and follows the provided instructions - * to apply transforms etc. - */ +function withFragmentRefs(obj) { + var result = Object.assign({}, obj); + // Both refs describe the original Relay snapshot, before conversions. + result.fragmentRefs = Object.assign({}, obj); + result.updatableFragmentRefs = result.fragmentRefs; + return result; +} + function traverse( - fullInstructionMap, - currentPath, - currentObj, - instructionMap, + maps, + prefix, + obj, + instructions, converters, - nullableValue, - instructionPaths, - addFragmentOnRoot + nullable, + fragment, ) { - // We lazily set up a new object for each "level", as we don't want to mutate - // what comes back from the Relay store, and nor do we want to create new - // objects unless we need to. And we only need to when we need to change - // something. - var newObj; - - if (addFragmentOnRoot) { - newObj = getNewObj(newObj, currentObj); - newObj.fragmentRefs = Object.assign({}, newObj); - newObj.updatableFragmentRefs = newObj.fragmentRefs; - } - - for (var key in currentObj) { - // Ensure we don't move into internal properties coming from Relay, with the - // exception of the few names allowed to start with double underscores - // ("__typename" and "__id"), and any Relay provided variables. + var result = fragment ? withFragmentRefs(obj) : undefined; + for (var key in obj) { + // Relay metadata may contain cycles and must remain opaque. if ( key.startsWith("__") && key !== "__typename" && @@ -64,361 +41,237 @@ function traverse( ) continue; - var isUnion = false; - var originalValue = currentObj[key]; - - // Instructions are stored by the path in the object where they apply - var thisPath = makeNewPath(currentPath, [key]); - var path = getPathName(thisPath); - - var instructions = instructionMap[path] || {}; - - if (currentObj[key] == null) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = nullableValue; - continue; - } - - if (currentObj[key] && currentObj[key].BS_PRIVATE_NESTED_SOME_NONE >= 0) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = currentObj[key]; - continue; - } - - var shouldConvertRootObj = - typeof instructions["r"] === "string" && - fullInstructionMap[instructions["r"]]; - - var shouldAddFragmentFn = instructions["f"] === ""; - - var shouldConvertEnum = - typeof instructions["e"] === "string" && !!converters[instructions["e"]]; - - // This is true for non-arrays that are to be converted - var shouldConvertCustomField = - typeof instructions["c"] === "string" && !!converters[instructions["c"]]; - - // This is true for arrays that are to be converted. This and the above - // won't be true at the same time. - var shouldConvertCustomFieldArray = - typeof instructions["ca"] === "string" && - !!converters[instructions["ca"]]; - - // Special case when this is a custom field that's an array. Ensures we - // don't accidentally move into the array when we're not supposed to. - if (shouldConvertCustomFieldArray && Array.isArray(currentObj[key])) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = currentObj[key].map(converters[instructions["ca"]]); - continue; - } - - var shouldBlockTraversal = typeof instructions["b"] === "string"; - var allowGoingIntoArray = shouldBlockTraversal - ? instructions["b"] === "a" - : true; - - if (shouldBlockTraversal && !allowGoingIntoArray) { - newObj = getNewObj(newObj, currentObj); + var value = obj[key]; + var converted; + if (value == null) { + converted = nullable; + } else if (value.BS_PRIVATE_NESTED_SOME_NONE >= 0) { continue; + } else { + // Carry the encoded prefix rather than allocating and joining path arrays. + var path = prefix + key; + var instruction = instructions[path]; + // Most selected scalar fields need no conversion. + if (instruction === undefined && typeof value !== "object") continue; + instruction = instruction || empty; + converted = convertField( + value, + instruction, + maps, + path, + instructions, + converters, + nullable, + ); } - - var shouldConvertUnion = - typeof instructions["u"] === "string" && !!converters[instructions["u"]]; - - /** - * Handle arrays - */ - - // Special case when this is a custom field that's an array. Ensures we - // don't accidentally move into the array when we're not supposed to. - if (shouldConvertCustomField && Array.isArray(currentObj[key])) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = converters[instructions["c"]](originalValue); - continue; + if (converted !== value) { + if (result === undefined) result = Object.assign({}, obj); + result[key] = converted; } + } + return result === undefined ? obj : result; +} - if (Array.isArray(currentObj[key])) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = currentObj[key].map(function (v) { - if (v == null) { - return nullableValue; - } - if (shouldConvertRootObj) { - return traverser( - unwrapInputUnion(v), - fullInstructionMap, - converters, - nullableValue, - instructions["r"] - ); - } - - if (shouldConvertEnum) { - return converters[instructions["e"]](v); - } - - if (shouldConvertCustomField) { - return converters[instructions["c"]](v); - } - - if (shouldConvertUnion && v != null && typeof v === "object") { - var typename = getTypename(v); - - if (typename != null) { - isUnion = true; - var unionObj = v; - - // Means we're wrapping, and this will be a ReScript value. - if (nullableValue === null) { - // Convert it back to a flat JS value - unionObj = converters[instructions["u"]](v); - } - - var newPath = makeNewPath(currentPath, [key, typename]); - - var unionRootHasFragment = - (instructionMap[getPathName(newPath)] || {}).f === ""; - - var traversedValue = traverse( - fullInstructionMap, - newPath, - unionObj, - instructionMap, - converters, - nullableValue, - instructionPaths, - unionRootHasFragment - ); - - // Undefined means we're going from JS to ReScript, in which case we - // need to run the conversion here rather than earlier. - return nullableValue === undefined - ? converters[instructions["u"]](traversedValue) - : traversedValue; - } - } - - if (shouldAddFragmentFn && typeof v === "object" && !Array.isArray(v)) { - var objWithFragmentFn = Object.assign({}, v); - objWithFragmentFn.fragmentRefs = Object.assign({}, objWithFragmentFn); - objWithFragmentFn.updatableFragmentRefs = - objWithFragmentFn.fragmentRefs; - return objWithFragmentFn; - } - - return v; - }); - } else { - /** - * Handle normal values. - */ - var v = currentObj[key]; +function convertUnion( + value, + convert, + maps, + path, + instructions, + converters, + nullable, +) { + var unionPath = path + "_" + value.__typename; + var fragment = (instructions[unionPath] || empty).f === ""; + var raw = nullable === null ? convert(value) : value; + var result = traverse( + maps, + unionPath + "_", + raw, + instructions, + converters, + nullable, + fragment, + ); + return nullable === undefined ? convert(result) : result; +} - if (shouldConvertRootObj) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = traverser( - unwrapInputUnion(v), - fullInstructionMap, +function convertField( + value, + instruction, + maps, + path, + instructions, + converters, + nullable, +) { + var isArray = Array.isArray(value); + var customArray = + typeof instruction.ca === "string" && converters[instruction.ca]; + if (isArray && customArray) { + // GraphQL null elements belong to the list wrapper, not the scalar parser. + return value.map((item, index, array) => { + if (item == null) return nullable; + if (item.BS_PRIVATE_NESTED_SOME_NONE >= 0) return item; + return customArray(item, index, array); + }); + } + var blocked = typeof instruction.b === "string"; + if (blocked && instruction.b !== "a") return value; + + var custom = typeof instruction.c === "string" && converters[instruction.c]; + // A scalar can itself be represented by an array. Its result is always opaque. + if (isArray && custom) return custom(value); + + var root = typeof instruction.r === "string" && maps[instruction.r]; + var enumConverter = + typeof instruction.e === "string" && converters[instruction.e]; + var union = typeof instruction.u === "string" && converters[instruction.u]; + var fragment = instruction.f === ""; + + if (isArray) { + var result; + for (var i = 0; i < value.length; i++) { + // Match map's handling of sparse arrays. + if (!(i in value)) continue; + var item = value[i]; + var converted = item; + if (item == null) { + converted = nullable; + } else if (root) { + converted = traverser( + unwrapInputUnion(item), + maps, converters, - nullableValue, - instructions["r"] + nullable, + instruction.r, ); - continue; - } - - if (shouldConvertEnum) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = converters[instructions["e"]](v); - } - - if (shouldConvertCustomField) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = converters[instructions["c"]](v); - // Ensure that the custom scalar value itself isn't traversed more. - continue; - } - - if (shouldConvertUnion && v != null && typeof v === "object") { - var typename = getTypename(v); - - if (typename != null) { - isUnion = true; - var unionObj = v; - - // Means we're wrapping, and this will be a ReScript value. - if (nullableValue === null) { - // Convert it back to a flat JS value - unionObj = converters[instructions["u"]](v); - } - - var newPath = makeNewPath(currentPath, [key, typename]); - - var unionRootHasFragment = - (instructionMap[getPathName(newPath)] || {}).f === ""; - - var traversedValue = traverse( - fullInstructionMap, - newPath, - unionObj, - instructionMap, - converters, - nullableValue, - instructionPaths, - unionRootHasFragment - ); - - newObj = getNewObj(newObj, currentObj); - - newObj[key] = - // Undefined means we're going from JS to ReScript, in which case we - // need to run the conversion here rather than earlier. - nullableValue === undefined - ? converters[instructions["u"]](traversedValue) - : traversedValue; - } - } - - if (shouldAddFragmentFn && typeof v === "object" && !Array.isArray(v)) { - newObj = getNewObj(newObj, currentObj); - var objWithFragmentFn = Object.assign({}, v); - objWithFragmentFn.fragmentRefs = Object.assign({}, objWithFragmentFn); - objWithFragmentFn.updatableFragmentRefs = - objWithFragmentFn.fragmentRefs; - - newObj[key] = objWithFragmentFn; - } - } - - if (originalValue != null && !isUnion) { - var nextObj = (newObj && newObj[key]) || currentObj[key]; - - if (typeof nextObj === "object" && !Array.isArray(originalValue)) { - var traversedObj = traverse( - fullInstructionMap, - thisPath, - nextObj, - instructionMap, + } else if (enumConverter) { + converted = enumConverter(item); + } else if (union && typeof item === "object" && item.__typename != null) { + converted = convertUnion( + item, + union, + maps, + path, + instructions, converters, - nullableValue, - instructionPaths + nullable, ); - - if (traversedObj !== nextObj) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = traversedObj; - } - } else if (Array.isArray(originalValue) && !shouldBlockTraversal) { - newObj = getNewObj(newObj, currentObj); - newObj[key] = nextObj.map(function (o) { - if (typeof o === "object" && o != null && !Array.isArray(o)) { - return traverse( - fullInstructionMap, - thisPath, - o, - instructionMap, + } else if (typeof item === "object" && !Array.isArray(item)) { + converted = blocked + ? fragment + ? withFragmentRefs(item) + : item + : traverse( + maps, + path + "_", + item, + instructions, converters, - nullableValue, - instructionPaths + nullable, + fragment, ); - } else if (o == null) { - return nullableValue; - } else { - return o; - } - }); + } + if (converted !== item) { + if (result === undefined) result = value.slice(); + result[i] = converted; } } + return result === undefined ? value : result; } - return newObj || currentObj; + if (root) + return traverser( + unwrapInputUnion(value), + maps, + converters, + nullable, + instruction.r, + ); + if (custom) return custom(value); + if (enumConverter) return enumConverter(value); + if (typeof value === "object") { + if (union && value.__typename != null) { + return convertUnion( + value, + union, + maps, + path, + instructions, + converters, + nullable, + ); + } + return traverse( + maps, + path + "_", + value, + instructions, + converters, + nullable, + fragment, + ); + } + return value; } /** - * This function takes an object (snapshot from the Relay store) and applies a - * set of conversions deeply on the object (instructions coming from "converters"-prop). - * It converts nullable values either to null or undefined, and it wraps/unwraps enums - * and unions. - * - * It preserves structural integrity where possible, and return new objects where properties - * have been modified. + * Convert Relay snapshots (nullable=undefined) or write payloads (nullable=null). + * Instructions and converters are read on each call; neither they nor payloads + * are cached or mutated. Unchanged branches retain their original identity. */ -function traverser( - root, - instructionMaps_, - theConverters, - nullableValue, - rootObjectKey -) { - if (!root) { - return nullableValue; +function traverser(root, instructionMaps, converters, nullable, rootObjectKey) { + if (!root) return nullable; + var maps = instructionMaps || empty; + var instructions = maps[rootObjectKey || "__root"] || empty; + converters = converters || empty; + var rootInstruction = instructions[""] || empty; + var union = converters[rootInstruction.u]; + var fragment = rootInstruction.f === ""; + + function convertRoot(value) { + if (value == null) return nullable; + var prefix = ""; + var hasFragment = fragment; + if (union != null) { + prefix = value.__typename + "_"; + hasFragment = (instructions[value.__typename] || empty).f === ""; + } + var result = traverse( + maps, + prefix, + value, + instructions, + converters, + nullable, + hasFragment, + ); + return union != null ? union(result) : result; } - var instructionMaps = instructionMaps_ || {}; - var instructionMap = instructionMaps[rootObjectKey || "__root"] || {}; - - var converters = theConverters == null ? {} : theConverters; - var instructionPaths = Object.keys(instructionMap); - - // We'll add the fragmentRefs reference to the root if needed here. - var fragmentsOnRoot = (instructionMap[""] || {}).f === ""; - var unionRootConverter = converters[(instructionMap[""] || {}).u]; - if (Array.isArray(root)) { - return root.map(function (v) { - if (v == null) { - return nullableValue; - } - - var n = []; - - // Since a root level union is treated as a "new root level", we'll need - // to do a separate check here of whether there's a fragment on the root - // we need to account for, or not. - if (unionRootConverter != null) { - n = [v.__typename]; - fragmentsOnRoot = (instructionMap[v.__typename] || {}).f === ""; + var result; + for (var i = 0; i < root.length; i++) { + if (!(i in root)) continue; + var converted = convertRoot(root[i]); + if (converted !== root[i]) { + if (result === undefined) result = root.slice(); + result[i] = converted; } - - var traversedObj = traverse( - instructionMaps, - n, - v, - instructionMap, - converters, - nullableValue, - instructionPaths, - fragmentsOnRoot - ); - - return unionRootConverter != null - ? unionRootConverter(traversedObj) - : traversedObj; - }); - } - - var newObj = Object.assign({}, root); - - var n = []; - - // Same as in the union array check above - if there's a fragment in the new - // root created by the union, we need to account for that separately here. - if (unionRootConverter != null) { - n = [newObj.__typename]; - fragmentsOnRoot = (instructionMap[newObj.__typename] || {}).f === ""; + } + return result === undefined ? root : result; } + return convertRoot(root); +} - var v = traverse( - instructionMaps, - n, - newObj, - instructionMap, - converters, - nullableValue, - instructionPaths, - fragmentsOnRoot - ); - - return unionRootConverter != null ? unionRootConverter(v) : v; +function runConversion(convert, value) { + return convert(value); } -module.exports = { traverser }; +module.exports = { + traverser, + prepareConversion, + runConversion, + convertWithoutPlan, +}; diff --git a/packages/rescript-relay/yarn.lock b/packages/rescript-relay/yarn.lock index 714c2186..b3c8fa05 100644 --- a/packages/rescript-relay/yarn.lock +++ b/packages/rescript-relay/yarn.lock @@ -1181,6 +1181,136 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@esbuild/aix-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c" + integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA== + +"@esbuild/android-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752" + integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg== + +"@esbuild/android-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a" + integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg== + +"@esbuild/android-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16" + integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg== + +"@esbuild/darwin-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd" + integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg== + +"@esbuild/darwin-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e" + integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA== + +"@esbuild/freebsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe" + integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg== + +"@esbuild/freebsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3" + integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ== + +"@esbuild/linux-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977" + integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ== + +"@esbuild/linux-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9" + integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw== + +"@esbuild/linux-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0" + integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA== + +"@esbuild/linux-loong64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0" + integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng== + +"@esbuild/linux-mips64el@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd" + integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw== + +"@esbuild/linux-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869" + integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA== + +"@esbuild/linux-riscv64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6" + integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w== + +"@esbuild/linux-s390x@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663" + integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg== + +"@esbuild/linux-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306" + integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw== + +"@esbuild/netbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4" + integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg== + +"@esbuild/netbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076" + integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ== + +"@esbuild/openbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd" + integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A== + +"@esbuild/openbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679" + integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw== + +"@esbuild/openharmony-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d" + integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg== + +"@esbuild/sunos-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6" + integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w== + +"@esbuild/win32-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323" + integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg== + +"@esbuild/win32-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267" + integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ== + +"@esbuild/win32-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5" + integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA== + "@glennsl/rescript-fetch@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@glennsl/rescript-fetch/-/rescript-fetch-0.2.0.tgz#b72085d8bb19d8b9266aaf18d59c440ddceaf8dd" @@ -2342,6 +2472,38 @@ es-set-tostringtag@^2.1.0: has-tostringtag "^1.0.2" hasown "^2.0.2" +esbuild@0.25.12: + version "0.25.12" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5" + integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.12" + "@esbuild/android-arm" "0.25.12" + "@esbuild/android-arm64" "0.25.12" + "@esbuild/android-x64" "0.25.12" + "@esbuild/darwin-arm64" "0.25.12" + "@esbuild/darwin-x64" "0.25.12" + "@esbuild/freebsd-arm64" "0.25.12" + "@esbuild/freebsd-x64" "0.25.12" + "@esbuild/linux-arm" "0.25.12" + "@esbuild/linux-arm64" "0.25.12" + "@esbuild/linux-ia32" "0.25.12" + "@esbuild/linux-loong64" "0.25.12" + "@esbuild/linux-mips64el" "0.25.12" + "@esbuild/linux-ppc64" "0.25.12" + "@esbuild/linux-riscv64" "0.25.12" + "@esbuild/linux-s390x" "0.25.12" + "@esbuild/linux-x64" "0.25.12" + "@esbuild/netbsd-arm64" "0.25.12" + "@esbuild/netbsd-x64" "0.25.12" + "@esbuild/openbsd-arm64" "0.25.12" + "@esbuild/openbsd-x64" "0.25.12" + "@esbuild/openharmony-arm64" "0.25.12" + "@esbuild/sunos-x64" "0.25.12" + "@esbuild/win32-arm64" "0.25.12" + "@esbuild/win32-ia32" "0.25.12" + "@esbuild/win32-x64" "0.25.12" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"