diff --git a/package.json b/package.json index 4a78d84..42c10b3 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,7 @@ "dependencies": { "@0no-co/graphql.web": "^1.0.6", "@swan-io/boxed": "^3.0.0", - "@swan-io/request": "^1.0.6", - "ts-pattern": "^5.1.0" + "@swan-io/request": "^1.0.6" }, "peerDependencies": { "react": "^18.2.0" @@ -78,6 +77,7 @@ "react-dom": "^18.2.0", "tsup": "^8.0.2", "tsx": "^4.7.1", + "ts-pattern": "^5.1.0", "typescript": "^5.6.3", "vite": "^5.2.7", "vitest": "^1.4.0" diff --git a/src/cache/cache.ts b/src/cache/cache.ts index f3f6664..3a0ba4e 100644 --- a/src/cache/cache.ts +++ b/src/cache/cache.ts @@ -1,71 +1,65 @@ -import { - DocumentNode, - OperationDefinitionNode, - OperationTypeNode, -} from "@0no-co/graphql.web"; +import { DocumentNode } from "@0no-co/graphql.web"; import { Array, Option, Result } from "@swan-io/boxed"; -import { P, match } from "ts-pattern"; +import { getCacheEntryKey } from "../json/cacheEntryKey"; import { Connection, Edge } from "../types"; import { - DEEP_MERGE_DELETE, + CONNECTION_REF, + EDGES_KEY, + NODE_KEY, REQUESTED_KEYS, + TYPENAME_KEY, containsAll, - deepMerge, isRecord, serializeVariables, } from "../utils"; - -export const getCacheKeyFromJson = (json: unknown): Option => { - return match(json) - .with( - { __typename: P.select(P.union("Query", "Mutation", "Subscription")) }, - (name) => Option.Some(Symbol.for(name)), - ) - .with( - { __typename: P.select("name", P.string), id: P.select("id", P.string) }, - ({ name, id }) => Option.Some(Symbol.for(`${name}<${id}>`)), - ) - .otherwise(() => Option.None()); -}; +import type { CacheEntry } from "./entry"; export type SchemaConfig = { interfaceToTypes: Record; }; -export const getCacheKeyFromOperationNode = ( - operationNode: OperationDefinitionNode, -): Option => { - return match(operationNode.operation) - .with(OperationTypeNode.QUERY, () => Option.Some(Symbol.for("Query"))) - .with(OperationTypeNode.SUBSCRIPTION, () => - Option.Some(Symbol.for("Subscription")), - ) - .otherwise(() => Option.None()); +type ConnectionInfo = { + // useful for connection updates + cacheEntry: CacheEntry; + // to re-read from cache + document: DocumentNode; + variables: Record; + pathInQuery: PropertyKey[]; + fieldVariables: Record; }; export class ClientCache { - cache = new Map(); + cache = new Map(); operationCache = new Map< DocumentNode, Map>> >(); - schemaConfig: Record>; + interfaceToType: Record>; + connectionCache: Map; + connectionRefCount = -1; constructor(schemaConfig: SchemaConfig) { - this.schemaConfig = Object.fromEntries( + this.interfaceToType = Object.fromEntries( Object.entries(schemaConfig.interfaceToTypes).map(([key, value]) => [ key, new Set(value), ]), ); + this.connectionCache = new Map(); + } + + registerConnectionInfo(info: ConnectionInfo) { + const id = ++this.connectionRefCount; + this.connectionCache.set(id, info); + return id; } isTypeCompatible(typename: string, typeCondition: string) { if (typename === typeCondition) { return true; } - const compatibleTypes = this.schemaConfig[typeCondition]; + const compatibleTypes = this.interfaceToType[typeCondition]; if (compatibleTypes == undefined) { return false; } @@ -127,152 +121,20 @@ export class ClientCache { } } - getOrDefault(cacheKey: symbol): unknown { + getOrCreateEntry(cacheKey: symbol, defaultValue: CacheEntry): unknown { if (this.cache.has(cacheKey)) { return this.cache.get(cacheKey) as unknown; } else { - return {}; + const entry = defaultValue; + this.cache.set(cacheKey, entry); + return entry; } } - set(cacheKey: symbol, entry: unknown) { + set(cacheKey: symbol, entry: CacheEntry) { this.cache.set(cacheKey, entry); } - cacheIfEligible(value: T, requestedKeys: Set): symbol | T { - const cacheKeyOption = getCacheKeyFromJson(value); - if (cacheKeyOption.isSome()) { - const cacheKey = cacheKeyOption.get(); - const existingEntry = this.getOrDefault(cacheKey); - - this.set( - cacheKey, - deepMerge( - existingEntry, - isRecord(value) - ? { ...value, [REQUESTED_KEYS]: requestedKeys } - : value, - ), - ); - return cacheKey; - } else { - return value; - } - } - - updateFieldInClosestCachedAncestor({ - originalFieldName, - fieldNameWithArguments, - value, - path, - jsonPath, - ancestors, - variables, - queryVariables, - rootTypename, - selectedKeys, - documentNode, - }: { - originalFieldName: string; - fieldNameWithArguments: symbol | string; - value: unknown; - path: PropertyKey[]; - jsonPath: PropertyKey[]; - ancestors: unknown[]; - variables: Record; - queryVariables: Record; - rootTypename: string; - selectedKeys: Set; - documentNode: DocumentNode; - }) { - const ancestorsCopy = ancestors.concat(); - const pathCopy = path.concat(); - const writePath: PropertyKey[] = []; - - let ancestor; - - while ((ancestor = ancestorsCopy.pop())) { - const maybeCacheKey = getCacheKeyFromJson( - ancestorsCopy.length === 0 - ? { ...ancestor, __typename: rootTypename } - : ancestor, - ); - if (maybeCacheKey.isSome()) { - const cacheKey = maybeCacheKey.get(); - const existingEntry = this.getOrDefault(cacheKey); - - if (isRecord(value) && !Array.isArray(value)) { - if ( - typeof value.__typename === "string" && - value.__typename.endsWith("Connection") - ) { - value.__connectionCacheKey = cacheKey.description; - value.__connectionCachePath = [ - [...writePath, fieldNameWithArguments].map((item) => - typeof item === "symbol" ? { symbol: item.description } : item, - ), - ]; - value.__connectionArguments = variables; - // used to retrieve up to date data from pagination hook - value.__connectionQueryArguments = queryVariables; - value.__connectionDocumentNode = documentNode; - value.__connectionJsonPath = [...jsonPath, originalFieldName]; - } - value[REQUESTED_KEYS] = selectedKeys; - } - - const deepUpdate = writePath.reduce( - (acc, key) => { - return { - [key]: acc, - }; - }, - // remote original field - { - [originalFieldName]: DEEP_MERGE_DELETE, - [fieldNameWithArguments]: value, - }, - ); - - this.set(cacheKey, deepMerge(existingEntry, deepUpdate)); - - // When the cached ancestor is found, remove - break; - } - - writePath.push(pathCopy.pop() as PropertyKey); - } - } - - unsafe__update( - cacheKey: symbol, - path: (symbol | string)[], - updater: (value: A) => A, - ) { - this.get(cacheKey).map((cachedAncestor) => { - const value = path.reduce>( - (acc, key) => - acc.flatMap((acc) => - Option.fromNullable(isRecord(acc) ? acc[key] : null), - ), - Option.fromNullable(cachedAncestor), - ); - - value.map((item) => { - const deepUpdate = path.reduce( - (acc, key) => { - return { - [key]: acc, - }; - }, - updater(item as A), - ); - - this.set(cacheKey, deepMerge(cachedAncestor, deepUpdate)); - }); - }); - } - updateConnection( connection: Connection, config: @@ -280,95 +142,63 @@ export class ClientCache { | { append: Edge[] } | { remove: string[] }, ) { - match(connection as unknown) - .with( - { - __connectionCacheKey: P.string, - __connectionCachePath: P.array( - P.array(P.union({ symbol: P.string }, P.string)), + if (connection == null) { + return; + } + if ( + CONNECTION_REF in connection && + typeof connection[CONNECTION_REF] === "number" + ) { + const connectionConfig = this.connectionCache.get( + connection[CONNECTION_REF], + ); + if (connectionConfig == null) { + return; + } + + if ("prepend" in config) { + const edges = config.prepend; + connectionConfig.cacheEntry[EDGES_KEY] = [ + ...Array.filterMap(edges, ({ node, __typename }) => + getCacheEntryKey(node).flatMap((key) => + // we can omit the requested fields here because the Connection contrains the fields + this.getFromCacheWithoutKey(key).map(() => ({ + [TYPENAME_KEY]: __typename, + [NODE_KEY]: key, + })), + ), ), - }, - ({ __connectionCacheKey, __connectionCachePath }) => { - const cacheKey = Symbol.for(__connectionCacheKey); - const cachePath = __connectionCachePath.map((path) => - path.map((item) => - typeof item === "string" ? item : Symbol.for(item.symbol), + ...(connectionConfig.cacheEntry[EDGES_KEY] as unknown[]), + ]; + return; + } + + if ("append" in config) { + const edges = config.append; + connectionConfig.cacheEntry[EDGES_KEY] = [ + ...(connectionConfig.cacheEntry[EDGES_KEY] as unknown[]), + ...Array.filterMap(edges, ({ node, __typename }) => + getCacheEntryKey(node).flatMap((key) => + // we can omit the requested fields here because the Connection contrains the fields + this.getFromCacheWithoutKey(key).map(() => ({ + [TYPENAME_KEY]: __typename, + [NODE_KEY]: key, + })), ), - ); - const typenameSymbol = Symbol.for("__typename"); - const edgesSymbol = Symbol.for("edges"); - const nodeSymbol = Symbol.for("node"); - match(config) - .with({ prepend: P.select(P.nonNullable) }, (edges) => { - const firstPath = cachePath[0]; - if (firstPath != null) { - this.unsafe__update(cacheKey, firstPath, (value) => { - if (!isRecord(value) || !Array.isArray(value[edgesSymbol])) { - return value; - } - return { - ...value, - [edgesSymbol]: [ - ...Array.filterMap(edges, ({ node, __typename }) => - getCacheKeyFromJson(node).flatMap((key) => - // we can omit the requested fields here because the Connection contrains the fields - this.getFromCacheWithoutKey(key).map(() => ({ - [typenameSymbol]: __typename, - [nodeSymbol]: key, - })), - ), - ), - ...value[edgesSymbol], - ], - }; - }); - } - }) - .with({ append: P.select(P.nonNullable) }, (edges) => { - const lastPath = cachePath[cachePath.length - 1]; - if (lastPath != null) { - this.unsafe__update(cacheKey, lastPath, (value) => { - if (!isRecord(value) || !Array.isArray(value[edgesSymbol])) { - return value; - } - return { - ...value, - [edgesSymbol]: [ - ...value[edgesSymbol], - ...Array.filterMap(edges, ({ node, __typename }) => - getCacheKeyFromJson(node).flatMap((key) => - // we can omit the requested fields here because the Connection contrains the fields - this.getFromCacheWithoutKey(key).map(() => ({ - [typenameSymbol]: __typename, - [nodeSymbol]: key, - })), - ), - ), - ], - }; - }); - } - }) - .with({ remove: P.select(P.array()) }, (nodeIds) => { - cachePath.forEach((path) => { - this.unsafe__update(cacheKey, path, (value) => { - return isRecord(value) && Array.isArray(value[edgesSymbol]) - ? { - ...value, - [edgesSymbol]: value[edgesSymbol].filter((edge) => { - const node = edge[nodeSymbol] as symbol; - return !nodeIds.some((nodeId) => { - return node.description?.includes(`<${nodeId}>`); - }); - }), - } - : value; - }); - }); - }) - .exhaustive(); - }, - ) - .otherwise(() => {}); + ), + ]; + return; + } + const nodeIds = config.remove; + connectionConfig.cacheEntry[EDGES_KEY] = ( + connectionConfig.cacheEntry[EDGES_KEY] as unknown[] + ).filter((edge) => { + // @ts-expect-error fine + const node = edge[NODE_KEY] as symbol; + return !nodeIds.some((nodeId) => { + return node.description?.includes(`<${nodeId}>`); + }); + }); + } } } diff --git a/src/cache/entry.ts b/src/cache/entry.ts new file mode 100644 index 0000000..5b129ab --- /dev/null +++ b/src/cache/entry.ts @@ -0,0 +1,10 @@ +import { CONNECTION_REF, REQUESTED_KEYS } from "../utils"; + +export type CacheEntry = Record & { + [REQUESTED_KEYS]: Set; + [CONNECTION_REF]?: number; +}; + +export const createEmptyCacheEntry = (): CacheEntry => ({ + [REQUESTED_KEYS]: new Set(), +}); diff --git a/src/cache/read.ts b/src/cache/read.ts index b0f284c..4bd488c 100644 --- a/src/cache/read.ts +++ b/src/cache/read.ts @@ -7,14 +7,15 @@ import { SelectionSetNode, } from "@0no-co/graphql.web"; import { Array, Option, Result } from "@swan-io/boxed"; -import { P, match } from "ts-pattern"; import { addIdIfPreviousSelected, + getCacheKeyFromOperationNode, getFieldName, getFieldNameWithArguments, getSelectedKeys, isExcluded, } from "../graphql/ast"; +import { getTypename } from "../json/getTypename"; import { REQUESTED_KEYS, containsAll, @@ -23,7 +24,7 @@ import { isRecord, serializeVariables, } from "../utils"; -import { ClientCache, getCacheKeyFromOperationNode } from "./cache"; +import { ClientCache } from "./cache"; const getFromCacheOrReturnValue = ( cache: ClientCache, @@ -166,13 +167,7 @@ export const readOperationFromCache = ( if (selection.kind === Kind.INLINE_FRAGMENT) { const inlineFragmentNode = selection; const typeCondition = inlineFragmentNode.typeCondition?.name.value; - const dataTypename = match(data) - .with({ __typename: P.select(P.string) }, (name) => name) - .with( - { __typename: P.array({ __typename: P.select(P.string) }) }, - (name) => name[0], - ) - .otherwise(() => undefined); + const dataTypename = getTypename(data); if (typeCondition != null && dataTypename != null) { if (cache.isTypeCompatible(dataTypename, typeCondition)) { @@ -290,8 +285,9 @@ export const optimizeQuery = ( const nextSelections = Array.filterMap( selections.selections, (selection) => { - return match(selection) - .with({ kind: Kind.FIELD }, (fieldNode) => { + switch (selection.kind) { + case Kind.FIELD: { + const fieldNode = selection; const fieldNameWithArguments = getFieldNameWithArguments( fieldNode, variables, @@ -375,8 +371,9 @@ export const optimizeQuery = ( } else { return Option.Some(fieldNode); } - }) - .with({ kind: Kind.INLINE_FRAGMENT }, (inlineFragmentNode) => { + } + case Kind.INLINE_FRAGMENT: { + const inlineFragmentNode = selection; return traverse( inlineFragmentNode.selectionSet, data as Record, @@ -385,11 +382,10 @@ export const optimizeQuery = ( (selectionSet) => ({ ...inlineFragmentNode, selectionSet }) as InlineFragmentNode, ); - }) - .with({ kind: Kind.FRAGMENT_SPREAD }, () => { + } + default: return Option.None(); - }) - .exhaustive(); + } }, ); if (nextSelections.length > 0) { diff --git a/src/cache/write.ts b/src/cache/write.ts index 4c1281f..68364e0 100644 --- a/src/cache/write.ts +++ b/src/cache/write.ts @@ -1,18 +1,19 @@ import { - DocumentNode, Kind, OperationTypeNode, - SelectionSetNode, + type FieldNode, + type SelectionSetNode, } from "@0no-co/graphql.web"; -import { match } from "ts-pattern"; +import type { DocumentNode } from "graphql"; import { extractArguments, getFieldName, getFieldNameWithArguments, - getSelectedKeys, } from "../graphql/ast"; -import { isRecord } from "../utils"; -import { ClientCache } from "./cache"; +import { getCacheEntryKey } from "../json/cacheEntryKey"; +import { CONNECTION_REF, isRecord, REQUESTED_KEYS } from "../utils"; +import { type ClientCache } from "./cache"; +import { createEmptyCacheEntry, type CacheEntry } from "./entry"; export const writeOperationToCache = ( cache: ClientCache, @@ -20,168 +21,154 @@ export const writeOperationToCache = ( response: unknown, variables: Record, ) => { - const traverse = ( - selections: SelectionSetNode, - data: unknown[], - path: PropertyKey[] = [], - jsonPath: PropertyKey[] = [], - rootTypename: string, + const registerConnection = ( + cacheEntry: CacheEntry, + pathInQuery: PropertyKey[], + fieldVariables: Record, ) => { - selections.selections.forEach((selection) => { - match(selection) - .with({ kind: Kind.FIELD }, (fieldNode) => { - const originalFieldName = getFieldName(fieldNode); - const fieldNameWithArguments = getFieldNameWithArguments( - fieldNode, - variables, - ); - const fieldArguments = extractArguments(fieldNode, variables); + if (cacheEntry[CONNECTION_REF]) { + return; + } + const id = cache.registerConnectionInfo({ + cacheEntry, + variables, + pathInQuery, + fieldVariables, + document, + }); + cacheEntry[CONNECTION_REF] = id; + }; - const parent = data[data.length - 1] as Record; - const fieldValue = parent[originalFieldName]; - const selectedKeys = getSelectedKeys(fieldNode, variables); + const cacheField = ( + field: FieldNode, + parentJson: Record, + parentCache: CacheEntry, + path: PropertyKey[], + ) => { + const originalFieldName = getFieldName(field); + const fieldNameWithArguments = getFieldNameWithArguments(field, variables); + const fieldValue = parentJson[originalFieldName]; - if (fieldValue != undefined) { - if (Array.isArray(fieldValue)) { - cache.updateFieldInClosestCachedAncestor({ - originalFieldName, - fieldNameWithArguments, - value: fieldValue, - path, - jsonPath, - ancestors: data, - variables: fieldArguments, - queryVariables: variables, - rootTypename, - selectedKeys, - documentNode: document, - }); + if (!Array.isArray(parentCache)) { + parentCache[REQUESTED_KEYS].add(fieldNameWithArguments); + } - const nextValue = Array(fieldValue.length); + // either scalar type with no selection, or a null/undefined value + const subSelectionSet = field.selectionSet; + if (subSelectionSet === undefined || fieldValue == null) { + parentCache[fieldNameWithArguments] = fieldValue; + return; + } + // array with selection + if (Array.isArray(fieldValue)) { + const arrayCache = + parentCache[fieldNameWithArguments] ?? Array(fieldValue.length); + if (parentCache[fieldNameWithArguments] == undefined) { + parentCache[fieldNameWithArguments] = arrayCache; + } + fieldValue.forEach((item, index) => { + if (item == null) { + // @ts-expect-error It's fine + arrayCache[index] = item; + return; + } + const cacheKey = getCacheEntryKey(item); + const cacheEntry = cacheKey.map((key) => + cache.getOrCreateEntry(key, createEmptyCacheEntry()), + ); + const cacheObject = cacheEntry.getOr( + // @ts-expect-error It's fine + arrayCache[index] ?? createEmptyCacheEntry(), + ) as CacheEntry; - cache.updateFieldInClosestCachedAncestor({ - originalFieldName, - fieldNameWithArguments, - value: nextValue, - path: [...path, fieldNameWithArguments], - jsonPath: [...jsonPath, originalFieldName], - ancestors: [...data, fieldValue], - variables: fieldArguments, - queryVariables: variables, - rootTypename, - selectedKeys, - documentNode: document, - }); + // @ts-expect-error It's fine + const cacheValueInParent = cacheKey.getOr(cacheObject); + // @ts-expect-error It's fine + arrayCache[index] = cacheValueInParent; - fieldValue.forEach((item: unknown, index: number) => { - const value = cache.cacheIfEligible(item, selectedKeys); + cacheSelectionSet(subSelectionSet, item, cacheObject, [ + ...path, + originalFieldName, + index, + ]); + }); + return; + } + // object with selection + const record = fieldValue as Record; + const cacheKey = getCacheEntryKey(record); + const cacheEntry = cacheKey.map((key) => + cache.getOrCreateEntry(key, createEmptyCacheEntry()), + ); + const cacheObject = cacheEntry.getOr( + parentCache[fieldNameWithArguments] ?? createEmptyCacheEntry(), + ) as CacheEntry; - cache.updateFieldInClosestCachedAncestor({ - originalFieldName: index.toString(), - fieldNameWithArguments: index.toString(), - value, - path: [...path, fieldNameWithArguments], - jsonPath: [...jsonPath, originalFieldName], - ancestors: [...data, fieldValue], - variables: fieldArguments, - queryVariables: variables, - rootTypename, - selectedKeys, - documentNode: document, - }); + // @ts-expect-error It's fine + const cacheValueInParent = cacheKey.getOr(cacheObject); + parentCache[fieldNameWithArguments] = cacheValueInParent; - if (isRecord(item)) { - traverse( - fieldNode.selectionSet!, - [...data, fieldValue, item], - [...path, fieldNameWithArguments, index.toString()], - [...jsonPath, originalFieldName, index.toString()], - rootTypename, - ); - } - }); - } else { - const value = cache.cacheIfEligible(fieldValue, selectedKeys); + if ( + typeof record.__typename === "string" && + record.__typename.endsWith("Connection") + ) { + registerConnection( + cacheObject, + [...path, originalFieldName], + extractArguments(field, variables), + ); + } - cache.updateFieldInClosestCachedAncestor({ - originalFieldName, - fieldNameWithArguments, - value, - path, - jsonPath, - ancestors: data, - variables: fieldArguments, - queryVariables: variables, - rootTypename, - selectedKeys, - documentNode: document, - }); + return cacheSelectionSet(subSelectionSet, record, cacheObject, [ + ...path, + originalFieldName, + ]); + }; - if (isRecord(fieldValue) && fieldNode.selectionSet != undefined) { - traverse( - fieldNode.selectionSet, - [...data, fieldValue], - [...path, fieldNameWithArguments], - [...jsonPath, originalFieldName], - rootTypename, - ); - } - } - } else { - if (originalFieldName in parent) { - cache.updateFieldInClosestCachedAncestor({ - originalFieldName, - fieldNameWithArguments, - value: fieldValue, - path, - jsonPath, - ancestors: data, - variables: fieldArguments, - queryVariables: variables, - rootTypename, - selectedKeys, - documentNode: document, - }); - } - } - }) - .with({ kind: Kind.INLINE_FRAGMENT }, (inlineFragmentNode) => { - traverse( - inlineFragmentNode.selectionSet, - data, - path, - jsonPath, - rootTypename, - ); - }) - .with({ kind: Kind.FRAGMENT_SPREAD }, () => { - // ignore, those are stripped - }) - .exhaustive(); - }); + const cacheSelectionSet = ( + selectionSet: SelectionSetNode, + json: Record, + cached: CacheEntry, + path: PropertyKey[], + ) => { + for (const selection of selectionSet.selections) { + switch (selection.kind) { + case Kind.INLINE_FRAGMENT: + cacheSelectionSet(selection.selectionSet, json, cached, path); + continue; + case Kind.FIELD: + cacheField(selection, json, cached, path); + continue; + default: + continue; + } + } }; document.definitions.forEach((definition) => { if (definition.kind === Kind.OPERATION_DEFINITION) { // Root __typename can vary, but we can't guess it from the document alone - const rootTypename = match(definition.operation) - .with(OperationTypeNode.QUERY, () => "Query") - .with(OperationTypeNode.SUBSCRIPTION, () => "Subscription") - .with(OperationTypeNode.MUTATION, () => "Mutation") - .exhaustive(); + const operationName = + definition.operation === OperationTypeNode.QUERY + ? "Query" + : definition.operation === OperationTypeNode.SUBSCRIPTION + ? "Subscription" + : "Mutation"; + + if (!isRecord(response)) { + return; + } - cache.cacheIfEligible( - isRecord(response) - ? { - ...response, - __typename: rootTypename, - } - : response, - getSelectedKeys(definition, variables), + const cacheEntry = cache.getOrCreateEntry( + Symbol.for(operationName), + createEmptyCacheEntry(), + ); + return cacheSelectionSet( + definition.selectionSet, + response, + cacheEntry as CacheEntry, + [], ); - traverse(definition.selectionSet, [response], [], [], rootTypename); } }); - - return cache; }; diff --git a/src/client.ts b/src/client.ts index d430638..8399755 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,7 +1,6 @@ -import { DocumentNode, GraphQLError } from "@0no-co/graphql.web"; +import { DocumentNode } from "@0no-co/graphql.web"; import { Future, Option, Result } from "@swan-io/boxed"; import { Request, badStatusToError, emptyToError } from "@swan-io/request"; -import { P, match } from "ts-pattern"; import { ClientCache, SchemaConfig } from "./cache/cache"; import { optimizeQuery, readOperationFromCache } from "./cache/read"; import { writeOperationToCache } from "./cache/write"; @@ -60,19 +59,17 @@ const defaultMakeRequest: MakeRequest = ({ }) .mapOkToResult(badStatusToError) .mapOkToResult(emptyToError) - .mapOkToResult((payload) => - match(payload as unknown) - .returnType< - Result - >() - .with({ errors: P.select(P.array()) }, (errors) => - Result.Error(errors.map(parseGraphQLError)), - ) - .with({ data: P.select(P.nonNullable) }, (data) => Result.Ok(data)) - .otherwise((response) => - Result.Error(new InvalidGraphQLResponseError(response)), - ), - ); + .mapOkToResult((payload) => { + if (payload != null && typeof payload === "object") { + if ("errors" in payload && Array.isArray(payload.errors)) { + return Result.Error(payload.errors.map(parseGraphQLError)); + } + if ("data" in payload && payload.data != null) { + return Result.Ok(payload.data); + } + } + return Result.Error(new InvalidGraphQLResponseError(payload)); + }); }; type ConnectionUpdate = [ @@ -262,29 +259,22 @@ export class Client { ) { const variablesAsRecord = variables as Record; const transformedDocument = this.getTransformedDocument(document); + const cached = this.cache.getOperationFromCache( + transformedDocument, + variablesAsRecord, + ); - return match({ - cached: this.cache.getOperationFromCache( - transformedDocument, - variablesAsRecord, - ), - normalize, - }) - .with( - { cached: Option.P.Some(Result.P.Error(P._)) }, - (value) => value.cached, - ) - .with( - { cached: Option.P.Some(Result.P.Ok(P._)), normalize: false }, - (value) => value.cached, - ) - .otherwise(() => - readOperationFromCache( - this.cache, - transformedDocument, - variablesAsRecord, - ), - ); + if (cached.isSome() && cached.get().isError()) { + return cached; + } + if (cached.isSome() && cached.get().isOk() && normalize === false) { + return cached; + } + return readOperationFromCache( + this.cache, + transformedDocument, + variablesAsRecord, + ); } query( diff --git a/src/errors.ts b/src/errors.ts index c41c2e7..e4917f3 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -5,7 +5,6 @@ import { NetworkError, TimeoutError, } from "@swan-io/request"; -import { P, match } from "ts-pattern"; export type ClientError = | NetworkError @@ -26,38 +25,39 @@ export class InvalidGraphQLResponseError extends Error { } export const parseGraphQLError = (error: unknown): GraphQLError => { - return match(error) - .with( - { - message: P.string, - nodes: P.optional(P.any), - source: P.optional(P.any), - positions: P.optional(P.any), - path: P.optional(P.any), - error: P.optional(P.any), - extensions: P.optional(P.any), - }, - ({ message, nodes, source, positions, path, error, extensions }) => { - const originalError = match(error) - .with({ message: P.string }, ({ message }) => new Error(message)) - .otherwise(() => undefined); - return new GraphQLError( - message, - nodes as ReadonlyArray | ASTNode | null | undefined, - source, - positions as readonly number[] | null | undefined, - path as readonly (string | number)[] | null | undefined, - originalError, - extensions as - | { - [extension: string]: unknown; - } - | null - | undefined, - ); - }, - ) - .otherwise((error) => new GraphQLError(JSON.stringify(error))); + if ( + typeof error === "object" && + error != null && + "message" in error && + typeof error.message === "string" + ) { + const graphqlError = error as Record & { + message: string; + }; + const originalError = + "error" in error && + typeof error.error === "object" && + error.error != null && + "message" in error.error && + typeof error.error.message === "string" + ? new Error(error.error.message) + : undefined; + return new GraphQLError( + graphqlError.message, + graphqlError.nodes as ReadonlyArray | ASTNode | null | undefined, + graphqlError.source, + graphqlError.positions as readonly number[] | null | undefined, + graphqlError.path as readonly (string | number)[] | null | undefined, + originalError, + graphqlError.extensions as + | { + [extension: string]: unknown; + } + | null + | undefined, + ); + } + return new GraphQLError(JSON.stringify(error)); }; type Flat = T extends (infer X)[] ? X : T; diff --git a/src/graphql/ast.ts b/src/graphql/ast.ts index 05d82bb..60066d7 100644 --- a/src/graphql/ast.ts +++ b/src/graphql/ast.ts @@ -7,13 +7,13 @@ import { InlineFragmentNode, Kind, OperationDefinitionNode, + OperationTypeNode, SelectionNode, SelectionSetNode, ValueNode, visit, } from "@0no-co/graphql.web"; import { Array, Option } from "@swan-io/boxed"; -import { P, match } from "ts-pattern"; /** * Returns a Set with all keys selected within the direct selection sets @@ -113,36 +113,29 @@ const extractValue = ( valueNode: ValueNode, variables: Record, ): unknown => { - return match(valueNode) - .with({ kind: Kind.NULL }, () => null) - .with( - { - kind: P.union( - Kind.INT, - Kind.FLOAT, - Kind.STRING, - Kind.BOOLEAN, - Kind.ENUM, - ), - }, - ({ value }) => value, - ) - .with({ kind: Kind.LIST }, ({ values }) => - values.map((value) => extractValue(value, variables)), - ) - .with({ kind: Kind.OBJECT }, ({ fields }) => - Object.fromEntries( - fields.map(({ name: { value: name }, value }) => [ + switch (valueNode.kind) { + case Kind.NULL: + return null; + case Kind.INT: + case Kind.FLOAT: + case Kind.STRING: + case Kind.BOOLEAN: + case Kind.ENUM: + return valueNode.value; + case Kind.LIST: + return valueNode.values.map((value) => extractValue(value, variables)); + case Kind.OBJECT: + return Object.fromEntries( + valueNode.fields.map(({ name: { value: name }, value }) => [ name, extractValue(value, variables), ]), - ), - ) - .with( - { kind: Kind.VARIABLE }, - ({ name: { value: name } }) => variables[name], - ) - .exhaustive(); + ); + case Kind.VARIABLE: + return variables[valueNode.name.value]; + default: + return null; + } }; /** @@ -257,17 +250,16 @@ export const getExecutableOperationName = (document: DocumentNode) => { }; const getIdFieldNode = (selection: SelectionNode): Option => { - return match(selection) - .with({ kind: Kind.FIELD }, (fieldNode) => - fieldNode.name.value === "id" ? Option.Some(fieldNode) : Option.None(), - ) - .with({ kind: Kind.INLINE_FRAGMENT }, (inlineFragmentNode) => { - return Array.findMap( - inlineFragmentNode.selectionSet.selections, - getIdFieldNode, - ); - }) - .otherwise(() => Option.None()); + switch (selection.kind) { + case Kind.FIELD: + return selection.name.value === "id" + ? Option.Some(selection) + : Option.None(); + case Kind.INLINE_FRAGMENT: + return Array.findMap(selection.selectionSet.selections, getIdFieldNode); + default: + return Option.None(); + } }; export const addIdIfPreviousSelected = ( @@ -315,3 +307,16 @@ export const isExcluded = ( }), ); }; + +export const getCacheKeyFromOperationNode = ( + operationNode: OperationDefinitionNode, +): Option => { + switch (operationNode.operation) { + case OperationTypeNode.QUERY: + return Option.Some(Symbol.for("Query")); + case OperationTypeNode.SUBSCRIPTION: + return Option.Some(Symbol.for("Subscription")); + default: + return Option.None(); + } +}; diff --git a/src/json/cacheEntryKey.ts b/src/json/cacheEntryKey.ts new file mode 100644 index 0000000..3a2cc05 --- /dev/null +++ b/src/json/cacheEntryKey.ts @@ -0,0 +1,18 @@ +import { Option } from "@swan-io/boxed"; + +const OPERATION_TYPES = new Set(["Query", "Mutation", "Subscription"]); + +export const getCacheEntryKey = (json: unknown): Option => { + if (typeof json === "object" && json != null) { + if ("__typename" in json && typeof json.__typename === "string") { + const typename = json.__typename; + if (OPERATION_TYPES.has(typename)) { + return Option.Some(Symbol.for(typename)); + } + if ("id" in json && typeof json.id === "string") { + return Option.Some(Symbol.for(`${typename}<${json.id}>`)); + } + } + } + return Option.None(); +}; diff --git a/src/json/getTypename.ts b/src/json/getTypename.ts new file mode 100644 index 0000000..b71a044 --- /dev/null +++ b/src/json/getTypename.ts @@ -0,0 +1,10 @@ +export const getTypename = (json: unknown): string | undefined => { + if (typeof json === "object" && json != null) { + if (Array.isArray(json)) { + return getTypename(json[0]); + } + if ("__typename" in json && typeof json.__typename === "string") { + return json.__typename; + } + } +}; diff --git a/src/react/usePagination.ts b/src/react/usePagination.ts index 003d937..4bdaa51 100644 --- a/src/react/usePagination.ts +++ b/src/react/usePagination.ts @@ -1,9 +1,7 @@ -import { DocumentNode } from "@0no-co/graphql.web"; -import { AsyncData, Option, Result } from "@swan-io/boxed"; +import { Array, AsyncData, Option, Result } from "@swan-io/boxed"; import { useCallback, useContext, useRef, useSyncExternalStore } from "react"; -import { match } from "ts-pattern"; import { Connection } from "../types"; -import { deepEqual, isRecord, serializeVariables } from "../utils"; +import { CONNECTION_REF, deepEqual } from "../utils"; import { ClientContext } from "./ClientContext"; type mode = "before" | "after"; @@ -19,11 +17,7 @@ const mergeConnection = >( if (previous == null) { return next; } - if ("__connectionArguments" in next && isRecord(next.__connectionArguments)) { - if (next.__connectionArguments[mode] == null) { - return next; - } - } + if ( mode === "after" && next.pageInfo.endCursor === previous.pageInfo.endCursor @@ -39,120 +33,71 @@ const mergeConnection = >( return { ...next, - __connectionCachePath: match(mode) - .with("before", () => [ - ...("__connectionCachePath" in next && - Array.isArray(next.__connectionCachePath) - ? next.__connectionCachePath - : []), - ...("__connectionCachePath" in previous && - Array.isArray(previous.__connectionCachePath) - ? previous.__connectionCachePath - : []), - ]) - .with("after", () => [ - ...("__connectionCachePath" in previous && - Array.isArray(previous.__connectionCachePath) - ? previous.__connectionCachePath - : []), - ...("__connectionCachePath" in next && - Array.isArray(next.__connectionCachePath) - ? next.__connectionCachePath - : []), - ]) - .exhaustive(), - edges: match(mode) - .with("before", () => [...(next.edges ?? []), ...(previous.edges ?? [])]) - .with("after", () => [...(previous.edges ?? []), ...(next.edges ?? [])]) - .exhaustive(), - pageInfo: match(mode) - .with("before", () => ({ - hasPreviousPage: next.pageInfo.hasPreviousPage, - startCursor: next.pageInfo.startCursor, - hasNextPage: previous.pageInfo.hasNextPage, - endCursor: previous.pageInfo.endCursor, - })) - .with("after", () => ({ - hasPreviousPage: previous.pageInfo.hasPreviousPage, - startCursor: previous.pageInfo.startCursor, - hasNextPage: next.pageInfo.hasNextPage, - endCursor: next.pageInfo.endCursor, - })) - .exhaustive(), + edges: + mode === "before" + ? [...(next.edges ?? []), ...(previous.edges ?? [])] + : [...(previous.edges ?? []), ...(next.edges ?? [])], + pageInfo: + mode === "before" + ? { + hasPreviousPage: next.pageInfo.hasPreviousPage, + startCursor: next.pageInfo.startCursor, + hasNextPage: previous.pageInfo.hasNextPage, + endCursor: previous.pageInfo.endCursor, + } + : { + hasPreviousPage: previous.pageInfo.hasPreviousPage, + startCursor: previous.pageInfo.startCursor, + hasNextPage: next.pageInfo.hasNextPage, + endCursor: next.pageInfo.endCursor, + }, }; }; const createPaginationHook = (direction: mode) => { return >(connection: T): T => { const client = useContext(ClientContext); - const connectionArgumentsRef = useRef<[string, Record][]>( - [], - ); + const connectionRefs = useRef([]); + const lastReturnedValueRef = useRef>(Option.None()); - if (connection != null && "__connectionQueryArguments" in connection) { - const arg = connection.__connectionQueryArguments as Record< - string, - unknown - >; - const serializedArg = serializeVariables(arg); + if (connection == null) { + connectionRefs.current = []; + } else { if ( - !connectionArgumentsRef.current.find( - ([serialized]) => serializedArg === serialized, - ) + CONNECTION_REF in connection && + typeof connection[CONNECTION_REF] === "number" && + !connectionRefs.current.includes(connection[CONNECTION_REF]) ) { - // if `before` or `after` argument is `null`, we're on the first page, - // reset the pagination - if ( - "__connectionArguments" in connection && - isRecord(connection.__connectionArguments) && - connection.__connectionArguments[direction] == null - ) { - connectionArgumentsRef.current = [[serializedArg, arg]]; - } else { - connectionArgumentsRef.current = [ - ...connectionArgumentsRef.current, - [serializedArg, arg], - ]; - } + connectionRefs.current.push(connection[CONNECTION_REF]); } } - const jsonPath = useRef( - connection != null && "__connectionJsonPath" in connection - ? (connection.__connectionJsonPath as string[]) - : [], - ); - - const documentNode = - connection != null && "__connectionDocumentNode" in connection - ? (connection.__connectionDocumentNode as DocumentNode) - : undefined; - - const lastReturnedValueRef = useRef>(Option.None()); - // Get fresh data from cache const getSnapshot = useCallback(() => { - if (documentNode == null) { - return Option.None(); - } const value = Option.all( - connectionArgumentsRef.current.map(([, args]) => - client.readFromCache(documentNode, args, {}), + Array.filterMap(connectionRefs.current, (id) => + Option.fromNullable(client.cache.connectionCache.get(id)), + ).flatMap((info) => + client + .readFromCache(info.document, info.variables, {}) + .map((query) => + query.map((query) => ({ query, pathInQuery: info.pathInQuery })), + ), ), ) .map(Result.all) .flatMap((x) => x.toOption()) .map((queries) => - queries.map((query) => - jsonPath.current.reduce( + queries.map(({ query, pathInQuery }) => { + return pathInQuery.reduce( (acc, key) => acc != null && typeof acc === "object" && key in acc ? // @ts-expect-error indexable acc[key] : null, query, - ), - ), + ); + }), ) as Option; if (!deepEqual(value, lastReturnedValueRef.current)) { lastReturnedValueRef.current = value; @@ -160,7 +105,7 @@ const createPaginationHook = (direction: mode) => { } else { return lastReturnedValueRef.current; } - }, [client, documentNode]); + }, [client]); const data = useSyncExternalStore( (func) => client.subscribe(func), diff --git a/src/utils.ts b/src/utils.ts index d0611cd..0810f85 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,57 +1,10 @@ -export const DEEP_MERGE_DELETE = Symbol.for("DEEP_MERGE_DELETE"); - export const REQUESTED_KEYS = Symbol.for("__requestedKeys"); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const deepMerge = (target: any, source: any): any => { - if (target instanceof Set && source instanceof Set) { - return new Set([...target, ...source]); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const next: any = Array.isArray(target) - ? Array(target.length) - : Array.isArray(source) - ? Array(source.length) - : {}; - - Object.getOwnPropertyNames(target).forEach((name) => { - // instruction to remove existing field - if (source[name] !== DEEP_MERGE_DELETE) { - next[name] = target[name]; - } - }); - - Object.getOwnPropertySymbols(target).forEach((name) => { - // instruction to remove existing field - if (source[name] !== DEEP_MERGE_DELETE) { - next[name] = target[name]; - } - }); - - Object.getOwnPropertyNames(source).forEach((name) => { - // instruction to remove existing field - if (source[name] !== DEEP_MERGE_DELETE) { - if (isRecord(next[name]) && isRecord(source[name])) { - next[name] = deepMerge(next[name], source[name]); - } else { - next[name] = source[name]; - } - } - }); +export const CONNECTION_REF = "__connectionRef"; - Object.getOwnPropertySymbols(source).forEach((name) => { - // instruction to remove existing field - if (source[name] !== DEEP_MERGE_DELETE) { - if (isRecord(next[name]) && isRecord(source[name])) { - next[name] = deepMerge(next[name], source[name]); - } else { - next[name] = source[name]; - } - } - }); - - return next; -}; +export const TYPENAME_KEY = Symbol.for("__typename"); +export const EDGES_KEY = Symbol.for("edges"); +export const NODE_KEY = Symbol.for("node"); export const containsAll = (a: Set, b: Set): boolean => { const keys = [...b.values()]; diff --git a/test/__snapshots__/cache.test.ts.snap b/test/__snapshots__/cache.test.ts.snap index b9f95b3..7af7432 100644 --- a/test/__snapshots__/cache.test.ts.snap +++ b/test/__snapshots__/cache.test.ts.snap @@ -12,553 +12,7 @@ Map { Symbol(__typename): "Query", Symbol(accountMembership({"id":"1"})): Symbol(AccountMembership), Symbol(accountMemberships({"first":"2"})): { - "__connectionArguments": { - "first": "2", - }, - "__connectionCacheKey": "Query", - "__connectionCachePath": [ - [ - { - "symbol": "accountMemberships({"first":"2"})", - }, - ], - ], - "__connectionDocumentNode": { - "definitions": [ - { - "directives": undefined, - "kind": "OperationDefinition", - "name": { - "kind": "Name", - "value": "App", - }, - "operation": "query", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "id", - }, - "value": { - "kind": "Variable", - "name": { - "kind": "Name", - "value": "id", - }, - }, - }, - ], - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "accountMembership", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "user", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "kind": "InlineFragment", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "firstName", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "lastName", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "identificationLevels", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "InlineFragment", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "expert", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "PVID", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "QES", - }, - "selectionSet": undefined, - }, - ], - }, - "typeCondition": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "IdentificationLevels", - }, - }, - }, - ], - }, - }, - ], - }, - "typeCondition": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "User", - }, - }, - }, - ], - }, - }, - ], - }, - }, - { - "alias": undefined, - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "first", - }, - "value": { - "kind": "IntValue", - "value": "2", - }, - }, - ], - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "accountMemberships", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "edges", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "node", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "account", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "name", - }, - "selectionSet": undefined, - }, - ], - }, - }, - { - "alias": { - "kind": "Name", - "value": "membershipUser", - }, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "user", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "lastName", - }, - "selectionSet": undefined, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - { - "alias": undefined, - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "id", - }, - "value": { - "block": false, - "kind": "StringValue", - "value": "e8d38e87-9862-47ef-b749-212ed566b955", - }, - }, - ], - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "supportingDocumentCollection", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "supportingDocuments", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "createdAt", - }, - "selectionSet": undefined, - }, - ], - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - ], - }, - }, - ], - }, - "variableDefinitions": [ - { - "defaultValue": undefined, - "directives": undefined, - "kind": "VariableDefinition", - "type": { - "kind": "NonNullType", - "type": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "ID", - }, - }, - }, - "variable": { - "kind": "Variable", - "name": { - "kind": "Name", - "value": "id", - }, - }, - }, - ], - }, - ], - "kind": "Document", - }, - "__connectionJsonPath": [ - "accountMemberships", - ], - "__connectionQueryArguments": { - "id": "1", - }, + "__connectionRef": 0, Symbol(__requestedKeys): Set { Symbol(__typename), Symbol(edges), @@ -686,553 +140,7 @@ Map { Symbol(__typename): "Query", Symbol(accountMembership({"id":"1"})): Symbol(AccountMembership), Symbol(accountMemberships({"first":"2"})): { - "__connectionArguments": { - "first": "2", - }, - "__connectionCacheKey": "Query", - "__connectionCachePath": [ - [ - { - "symbol": "accountMemberships({"first":"2"})", - }, - ], - ], - "__connectionDocumentNode": { - "definitions": [ - { - "directives": undefined, - "kind": "OperationDefinition", - "name": { - "kind": "Name", - "value": "App", - }, - "operation": "query", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "id", - }, - "value": { - "kind": "Variable", - "name": { - "kind": "Name", - "value": "id", - }, - }, - }, - ], - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "accountMembership", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "user", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "kind": "InlineFragment", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "firstName", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "lastName", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "identificationLevels", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "InlineFragment", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "expert", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "PVID", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "QES", - }, - "selectionSet": undefined, - }, - ], - }, - "typeCondition": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "IdentificationLevels", - }, - }, - }, - ], - }, - }, - ], - }, - "typeCondition": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "User", - }, - }, - }, - ], - }, - }, - ], - }, - }, - { - "alias": undefined, - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "first", - }, - "value": { - "kind": "IntValue", - "value": "2", - }, - }, - ], - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "accountMemberships", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "edges", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "node", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "account", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "name", - }, - "selectionSet": undefined, - }, - ], - }, - }, - { - "alias": { - "kind": "Name", - "value": "membershipUser", - }, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "user", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "lastName", - }, - "selectionSet": undefined, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - { - "alias": undefined, - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "id", - }, - "value": { - "block": false, - "kind": "StringValue", - "value": "e8d38e87-9862-47ef-b749-212ed566b955", - }, - }, - ], - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "supportingDocumentCollection", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "supportingDocuments", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "createdAt", - }, - "selectionSet": undefined, - }, - ], - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - ], - }, - }, - ], - }, - "variableDefinitions": [ - { - "defaultValue": undefined, - "directives": undefined, - "kind": "VariableDefinition", - "type": { - "kind": "NonNullType", - "type": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "ID", - }, - }, - }, - "variable": { - "kind": "Variable", - "name": { - "kind": "Name", - "value": "id", - }, - }, - }, - ], - }, - ], - "kind": "Document", - }, - "__connectionJsonPath": [ - "accountMemberships", - ], - "__connectionQueryArguments": { - "id": "1", - }, + "__connectionRef": 0, Symbol(__requestedKeys): Set { Symbol(__typename), Symbol(edges), @@ -1375,8 +283,8 @@ Map { Symbol(message), }, Symbol(__typename): "BindAccountMembershipSuccessPayload", - Symbol(message): "Account is in invalid status", Symbol(accountMembership): Symbol(AccountMembership), + Symbol(message): undefined, }, }, } @@ -1394,553 +302,7 @@ Map { Symbol(__typename): "Query", Symbol(accountMembership({"id":"1"})): Symbol(AccountMembership), Symbol(accountMemberships({"first":"2"})): { - "__connectionArguments": { - "first": "2", - }, - "__connectionCacheKey": "Query", - "__connectionCachePath": [ - [ - { - "symbol": "accountMemberships({"first":"2"})", - }, - ], - ], - "__connectionDocumentNode": { - "definitions": [ - { - "directives": undefined, - "kind": "OperationDefinition", - "name": { - "kind": "Name", - "value": "App", - }, - "operation": "query", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "id", - }, - "value": { - "kind": "Variable", - "name": { - "kind": "Name", - "value": "id", - }, - }, - }, - ], - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "accountMembership", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "user", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "kind": "InlineFragment", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "firstName", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "lastName", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "identificationLevels", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "InlineFragment", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "expert", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "PVID", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "QES", - }, - "selectionSet": undefined, - }, - ], - }, - "typeCondition": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "IdentificationLevels", - }, - }, - }, - ], - }, - }, - ], - }, - "typeCondition": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "User", - }, - }, - }, - ], - }, - }, - ], - }, - }, - { - "alias": undefined, - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "first", - }, - "value": { - "kind": "IntValue", - "value": "2", - }, - }, - ], - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "accountMemberships", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "edges", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "node", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "account", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "name", - }, - "selectionSet": undefined, - }, - ], - }, - }, - { - "alias": { - "kind": "Name", - "value": "membershipUser", - }, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "user", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "lastName", - }, - "selectionSet": undefined, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - { - "alias": undefined, - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "id", - }, - "value": { - "block": false, - "kind": "StringValue", - "value": "e8d38e87-9862-47ef-b749-212ed566b955", - }, - }, - ], - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "supportingDocumentCollection", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "supportingDocuments", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "createdAt", - }, - "selectionSet": undefined, - }, - ], - }, - }, - { - "alias": undefined, - "arguments": undefined, - "directives": undefined, - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - "selectionSet": undefined, - }, - ], - }, - }, - ], - }, - "variableDefinitions": [ - { - "defaultValue": undefined, - "directives": undefined, - "kind": "VariableDefinition", - "type": { - "kind": "NonNullType", - "type": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "ID", - }, - }, - }, - "variable": { - "kind": "Variable", - "name": { - "kind": "Name", - "value": "id", - }, - }, - }, - ], - }, - ], - "kind": "Document", - }, - "__connectionJsonPath": [ - "accountMemberships", - ], - "__connectionQueryArguments": { - "id": "1", - }, + "__connectionRef": 1, Symbol(__requestedKeys): Set { Symbol(__typename), Symbol(edges), @@ -2094,8 +456,8 @@ Map { Symbol(message), }, Symbol(__typename): "BindAccountMembershipSuccessPayload", - Symbol(message): "Account is in invalid status", Symbol(accountMembership): Symbol(AccountMembership), + Symbol(message): undefined, }, }, } @@ -2185,6 +547,17 @@ Map { Symbol(taxIdentificationNumber): null, Symbol(employmentStatus): null, Symbol(monthlyIncome): null, + Symbol(legalRepresentativePersonalAddress): undefined, + Symbol(businessActivity): undefined, + Symbol(businessActivityDescription): undefined, + Symbol(companyType): undefined, + Symbol(isRegistered): undefined, + Symbol(monthlyPaymentVolume): undefined, + Symbol(name): undefined, + Symbol(typeOfRepresentation): undefined, + Symbol(registrationNumber): undefined, + Symbol(vatNumber): undefined, + Symbol(individualUltimateBeneficialOwners): undefined, }, Symbol(statusInfo): { Symbol(__requestedKeys): Set { @@ -2351,456 +724,7 @@ exports[`Write & read in cache 7`] = ` }, }, "accountMemberships": { - "__connectionArguments": { - "first": "2", - }, - "__connectionCacheKey": "Query", - "__connectionCachePath": [ - [ - { - "symbol": "accountMemberships({"first":"2"})", - }, - ], - ], - "__connectionDocumentNode": { - "definitions": [ - { - "kind": "OperationDefinition", - "name": { - "kind": "Name", - "value": "App", - }, - "operation": "query", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "id", - }, - "value": { - "kind": "Variable", - "name": { - "kind": "Name", - "value": "id", - }, - }, - }, - ], - "kind": "Field", - "name": { - "kind": "Name", - "value": "accountMembership", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "user", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - }, - { - "kind": "InlineFragment", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "firstName", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "lastName", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "identificationLevels", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "InlineFragment", - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "expert", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "PVID", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "QES", - }, - }, - ], - }, - "typeCondition": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "IdentificationLevels", - }, - }, - }, - ], - }, - }, - ], - }, - "typeCondition": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "User", - }, - }, - }, - ], - }, - }, - ], - }, - }, - { - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "first", - }, - "value": { - "kind": "IntValue", - "value": "2", - }, - }, - ], - "kind": "Field", - "name": { - "kind": "Name", - "value": "accountMemberships", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "edges", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "node", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "account", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "name", - }, - }, - ], - }, - }, - { - "alias": { - "kind": "Name", - "value": "membershipUser", - }, - "kind": "Field", - "name": { - "kind": "Name", - "value": "user", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "lastName", - }, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - { - "arguments": [ - { - "kind": "Argument", - "name": { - "kind": "Name", - "value": "id", - }, - "value": { - "block": false, - "kind": "StringValue", - "value": "e8d38e87-9862-47ef-b749-212ed566b955", - }, - }, - ], - "kind": "Field", - "name": { - "kind": "Name", - "value": "supportingDocumentCollection", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "supportingDocuments", - }, - "selectionSet": { - "kind": "SelectionSet", - "selections": [ - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "__typename", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "createdAt", - }, - }, - ], - }, - }, - { - "kind": "Field", - "name": { - "kind": "Name", - "value": "id", - }, - }, - ], - }, - }, - ], - }, - "variableDefinitions": [ - { - "kind": "VariableDefinition", - "type": { - "kind": "NonNullType", - "type": { - "kind": "NamedType", - "name": { - "kind": "Name", - "value": "ID", - }, - }, - }, - "variable": { - "kind": "Variable", - "name": { - "kind": "Name", - "value": "id", - }, - }, - }, - ], - }, - ], - "kind": "Document", - }, - "__connectionJsonPath": [ - "accountMemberships", - ], - "__connectionQueryArguments": { - "id": "1", - }, + "__connectionRef": 0, "__typename": "AccountMembershipConnection", "edges": [ {