From f999beb4d6d7be61928962fb8c11cb21c2219c8c Mon Sep 17 00:00:00 2001 From: Michael Hayes Date: Tue, 7 Feb 2023 09:21:07 -0800 Subject: [PATCH] fix rebase --- .../packages/plugin-relay/field-builder.ts | 303 ++++------ .../packages/plugin-relay/global-types.ts | 528 +++++------------- packages/deno/packages/plugin-relay/index.ts | 89 +-- .../plugin-relay/input-field-builder.ts | 109 ++-- .../deno/packages/plugin-relay/node-ref.ts | 6 + .../packages/plugin-relay/schema-builder.ts | 12 +- packages/deno/packages/plugin-relay/types.ts | 28 +- .../packages/plugin-relay/utils/internal.ts | 11 +- packages/plugin-relay/src/utils/internal.ts | 9 +- 9 files changed, 341 insertions(+), 754 deletions(-) create mode 100644 packages/deno/packages/plugin-relay/node-ref.ts diff --git a/packages/deno/packages/plugin-relay/field-builder.ts b/packages/deno/packages/plugin-relay/field-builder.ts index 1c721be49..686e8bf04 100644 --- a/packages/deno/packages/plugin-relay/field-builder.ts +++ b/packages/deno/packages/plugin-relay/field-builder.ts @@ -1,203 +1,124 @@ // @ts-nocheck import { GraphQLResolveInfo } from 'https://cdn.skypack.dev/graphql?dts'; -import { - assertArray, - FieldKind, - FieldNullability, - InputFieldMap, - InputShapeFromFields, - InterfaceRef, - ObjectRef, - RootFieldBuilder, - SchemaTypes, -} from '../core/index.ts'; -import { - ConnectionShape, - GlobalIDFieldOptions, - GlobalIDListFieldOptions, - GlobalIDShape, -} from './types.ts'; +import { assertArray, FieldKind, FieldNullability, InputFieldMap, InputShapeFromFields, InterfaceRef, ObjectRef, RootFieldBuilder, SchemaTypes, } from '../core/index.ts'; +import { ConnectionShape, GlobalIDFieldOptions, GlobalIDListFieldOptions, GlobalIDShape, } from './types.ts'; import { capitalize, resolveNodes } from './utils/index.ts'; import { internalDecodeGlobalID, internalEncodeGlobalID } from './utils/internal.ts'; -const fieldBuilderProto = RootFieldBuilder.prototype as PothosSchemaTypes.RootFieldBuilder< - SchemaTypes, - unknown, - FieldKind ->; -fieldBuilderProto.globalIDList = function globalIDList< - Args extends InputFieldMap, - Nullable extends FieldNullability<['ID']>, - ResolveReturnShape, ->({ - resolve, - ...options -}: GlobalIDListFieldOptions) { - const wrappedResolve = async ( - parent: unknown, - args: InputShapeFromFields, - context: object, - info: GraphQLResolveInfo, - ) => { - const result = await resolve(parent, args, context, info); - if (!result) { - return result; - } - assertArray(result); - if (Array.isArray(result)) { - return ((await Promise.all(result)) as (GlobalIDShape | null | undefined)[]).map( - (item) => - item == null || typeof item === 'string' - ? item - : internalEncodeGlobalID( - this.builder, - this.builder.configStore.getTypeConfig(item.type).name, - String(item.id), - context, - ), - ); - } - return null; - }; - return this.field({ - ...options, - type: ['ID'], - resolve: wrappedResolve as never, // resolve is not expected because we don't know FieldKind - }); +const fieldBuilderProto = RootFieldBuilder.prototype as PothosSchemaTypes.RootFieldBuilder; +fieldBuilderProto.globalIDList = function globalIDList, ResolveReturnShape>({ resolve, ...options }: GlobalIDListFieldOptions) { + const wrappedResolve = async (parent: unknown, args: InputShapeFromFields, context: object, info: GraphQLResolveInfo) => { + const result = await resolve(parent, args, context, info); + if (!result) { + return result; + } + assertArray(result); + if (Array.isArray(result)) { + return ((await Promise.all(result)) as (GlobalIDShape | null | undefined)[]).map((item) => item == null || typeof item === "string" + ? item + : internalEncodeGlobalID(this.builder, this.builder.configStore.getTypeConfig(item.type).name, String(item.id), context)); + } + return null; + }; + return this.field({ + ...options, + type: ["ID"], + resolve: wrappedResolve as never, // resolve is not expected because we don't know FieldKind + }); }; -fieldBuilderProto.globalID = function globalID< - Args extends InputFieldMap, - Nullable extends FieldNullability<'ID'>, - ResolveReturnShape, ->({ - resolve, - ...options -}: GlobalIDFieldOptions) { - const wrappedResolve = async ( - parent: unknown, - args: InputShapeFromFields, - context: object, - info: GraphQLResolveInfo, - ) => { - const result = await resolve(parent, args, context, info); - if (!result || typeof result === 'string') { - return result; - } - const item = result as unknown as GlobalIDShape; - return internalEncodeGlobalID( - this.builder, - this.builder.configStore.getTypeConfig(item.type).name, - String(item.id), - context, - ); - }; - return this.field({ - ...options, - type: 'ID', - resolve: wrappedResolve as never, // resolve is not expected because we don't know FieldKind - }); +fieldBuilderProto.globalID = function globalID, ResolveReturnShape>({ resolve, ...options }: GlobalIDFieldOptions) { + const wrappedResolve = async (parent: unknown, args: InputShapeFromFields, context: object, info: GraphQLResolveInfo) => { + const result = await resolve(parent, args, context, info); + if (!result || typeof result === "string") { + return result; + } + const item = result as unknown as GlobalIDShape; + return internalEncodeGlobalID(this.builder, this.builder.configStore.getTypeConfig(item.type).name, String(item.id), context); + }; + return this.field({ + ...options, + type: "ID", + resolve: wrappedResolve as never, // resolve is not expected because we don't know FieldKind + }); }; fieldBuilderProto.node = function node({ id, ...options }) { - return this.field<{}, InterfaceRef, unknown, Promise, true>({ - ...(options as {}), - type: this.builder.nodeInterfaceRef(), - nullable: true, - resolve: async (parent: unknown, args: {}, context: object, info: GraphQLResolveInfo) => { - const rawID = (await id(parent, args as never, context, info)) as unknown as - | GlobalIDShape - | string - | null - | undefined; - if (rawID == null) { - return null; - } - const globalID = - typeof rawID === 'string' - ? internalDecodeGlobalID(this.builder, rawID, context, info, true) - : rawID && { - id: String(rawID.id), - typename: this.builder.configStore.getTypeConfig(rawID.type).name, - }; - return (await resolveNodes(this.builder, context, info, [globalID]))[0]; - }, - }); + return this.field<{}, InterfaceRef, unknown, Promise, true>({ + ...(options as {}), + type: this.builder.nodeInterfaceRef(), + nullable: true, + resolve: async (parent: unknown, args: {}, context: object, info: GraphQLResolveInfo) => { + const rawID = (await id(parent, args as never, context, info)) as unknown as GlobalIDShape | string | null | undefined; + if (rawID == null) { + return null; + } + const globalID = typeof rawID === "string" + ? internalDecodeGlobalID(this.builder, rawID, context, info, true) + : rawID && { + id: String(rawID.id), + typename: this.builder.configStore.getTypeConfig(rawID.type).name, + }; + return (await resolveNodes(this.builder, context, info, [globalID]))[0]; + }, + }); }; fieldBuilderProto.nodeList = function nodeList({ ids, ...options }) { - return this.field({ - ...options, - nullable: { - list: false, - items: true, - }, - type: [this.builder.nodeInterfaceRef()], - resolve: async (parent: unknown, args: {}, context: object, info: GraphQLResolveInfo) => { - const rawIDList = await ids(parent, args as never, context, info); - assertArray(rawIDList); - if (!Array.isArray(rawIDList)) { - return []; - } - const rawIds = (await Promise.all(rawIDList)) as ( - | GlobalIDShape - | string - | null - | undefined - )[]; - const globalIds = rawIds.map((id) => - typeof id === 'string' - ? internalDecodeGlobalID(this.builder, id, context, info, true) - : id && { - id: String(id.id), - typename: this.builder.configStore.getTypeConfig(id.type).name, - }, - ); - return resolveNodes(this.builder, context, info, globalIds); - }, - }); -}; -fieldBuilderProto.connection = function connection( - { type, edgesNullable, nodeNullable, ...fieldOptions }, - connectionOptionsOrRef = {} as never, - edgeOptionsOrRef = {} as never, -) { - const connectionRef = - connectionOptionsOrRef instanceof ObjectRef - ? connectionOptionsOrRef - : this.builder.objectRef>( - 'Unnamed connection', - ); - const fieldRef = this.field({ - ...this.builder.options.relayOptions?.defaultConnectionFieldOptions, - ...fieldOptions, - type: connectionRef, - args: { - ...fieldOptions.args, - ...this.arg.connectionArgs(), - }, - resolve: fieldOptions.resolve as never, - } as never); - if (!(connectionOptionsOrRef instanceof ObjectRef)) { - this.builder.configStore.onFieldUse(fieldRef, (fieldConfig) => { - const connectionName = - connectionOptionsOrRef.name ?? - `${this.typename}${capitalize(fieldConfig.name)}${ - fieldConfig.name.toLowerCase().endsWith('connection') ? '' : 'Connection' - }`; - this.builder.connectionObject( - { - type, - edgesNullable, - nodeNullable, - ...connectionOptionsOrRef, - name: connectionName, + return this.field({ + ...options, + nullable: { + list: false, + items: true, + }, + type: [this.builder.nodeInterfaceRef()], + resolve: async (parent: unknown, args: {}, context: object, info: GraphQLResolveInfo) => { + const rawIDList = await ids(parent, args as never, context, info); + assertArray(rawIDList); + if (!Array.isArray(rawIDList)) { + return []; + } + const rawIds = (await Promise.all(rawIDList)) as (GlobalIDShape | string | null | undefined)[]; + const globalIds = rawIds.map((id) => typeof id === "string" + ? internalDecodeGlobalID(this.builder, id, context, info, true) + : id && { + id: String(id.id), + typename: this.builder.configStore.getTypeConfig(id.type).name, + }); + return resolveNodes(this.builder, context, info, globalIds); }, - edgeOptionsOrRef instanceof ObjectRef - ? edgeOptionsOrRef - : { - name: `${connectionName}Edge`, - ...edgeOptionsOrRef, - }, - ); - this.builder.configStore.associateRefWithName(connectionRef, connectionName); }); - } - return fieldRef as never; +}; +fieldBuilderProto.connection = function connection({ type, edgesNullable, nodeNullable, ...fieldOptions }, connectionOptionsOrRef = {} as never, edgeOptionsOrRef = {} as never) { + const connectionRef = connectionOptionsOrRef instanceof ObjectRef + ? connectionOptionsOrRef + : this.builder.objectRef>("Unnamed connection"); + const fieldRef = this.field({ + ...this.builder.options.relayOptions?.defaultConnectionFieldOptions, + ...fieldOptions, + type: connectionRef, + args: { + ...fieldOptions.args, + ...this.arg.connectionArgs(), + }, + resolve: fieldOptions.resolve as never, + } as never); + if (!(connectionOptionsOrRef instanceof ObjectRef)) { + this.builder.configStore.onFieldUse(fieldRef, (fieldConfig) => { + const connectionName = connectionOptionsOrRef.name ?? + `${this.typename}${capitalize(fieldConfig.name)}${fieldConfig.name.toLowerCase().endsWith("connection") ? "" : "Connection"}`; + this.builder.connectionObject({ + type, + edgesNullable, + nodeNullable, + ...connectionOptionsOrRef, + name: connectionName, + }, edgeOptionsOrRef instanceof ObjectRef + ? edgeOptionsOrRef + : { + name: `${connectionName}Edge`, + ...edgeOptionsOrRef, + }); + this.builder.configStore.associateRefWithName(connectionRef, connectionName); + }); + } + return fieldRef as never; }; diff --git a/packages/deno/packages/plugin-relay/global-types.ts b/packages/deno/packages/plugin-relay/global-types.ts index 519ebea41..55adf620a 100644 --- a/packages/deno/packages/plugin-relay/global-types.ts +++ b/packages/deno/packages/plugin-relay/global-types.ts @@ -1,401 +1,141 @@ // @ts-nocheck -import { - FieldKind, - FieldNullability, - FieldOptionsFromKind, - FieldRef, - FieldRequiredness, - InputFieldMap, - InputFieldRef, - InputFieldsFromShape, - InputShapeFromFields, - InputShapeFromTypeParam, - inputShapeKey, - InterfaceParam, - NormalizeArgs, - ObjectFieldsShape, - ObjectFieldThunk, - ObjectParam, - OutputShape, - OutputType, - ParentShape, - Resolver, - SchemaTypes, - ShapeFromTypeParam, -} from '../core/index.ts'; +import { FieldKind, FieldNullability, FieldOptionsFromKind, FieldRef, FieldRequiredness, InputFieldMap, InputFieldRef, InputFieldsFromShape, InputShapeFromFields, InputShapeFromTypeParam, inputShapeKey, InterfaceParam, NormalizeArgs, ObjectFieldsShape, ObjectFieldThunk, ObjectParam, OutputShape, OutputType, ParentShape, Resolver, SchemaTypes, ShapeFromTypeParam, } from '../core/index.ts'; import { NodeRef } from './node-ref.ts'; -import { - ConnectionShape, - ConnectionShapeForType, - ConnectionShapeFromResolve, - GlobalIDFieldOptions, - GlobalIDInputFieldOptions, - GlobalIDInputShape, - GlobalIDListFieldOptions, - GlobalIDListInputFieldOptions, - GlobalIDShape, - InputShapeWithClientMutationId, - NodeFieldOptions, - NodeListFieldOptions, - NodeObjectOptions, - PageInfoShape, - RelayMutationFieldOptions, - RelayMutationInputOptions, - RelayMutationPayloadOptions, - RelayPluginOptions, -} from './types.ts'; +import { ConnectionShape, ConnectionShapeForType, ConnectionShapeFromResolve, GlobalIDFieldOptions, GlobalIDInputFieldOptions, GlobalIDInputShape, GlobalIDListFieldOptions, GlobalIDListInputFieldOptions, GlobalIDShape, InputShapeWithClientMutationId, NodeFieldOptions, NodeListFieldOptions, NodeObjectOptions, PageInfoShape, RelayMutationFieldOptions, RelayMutationInputOptions, RelayMutationPayloadOptions, RelayPluginOptions, } from './types.ts'; import type { DefaultEdgesNullability, PothosRelayPlugin } from './index.ts'; declare global { - export namespace PothosSchemaTypes { - export interface Plugins { - relay: PothosRelayPlugin; - } - export interface SchemaBuilderOptions { - relayOptions: RelayPluginOptions; - } - export interface UserSchemaTypes { - Connection: {}; - DefaultEdgesNullability: FieldNullability<[unknown]>; - DefaultNodeNullability: boolean; - } - export interface ExtendDefaultTypes> { - Connection: PartialTypes['Connection'] & {}; - DefaultEdgesNullability: FieldNullability< - [unknown] - > extends PartialTypes['DefaultEdgesNullability'] - ? DefaultEdgesNullability - : PartialTypes['DefaultEdgesNullability'] & FieldNullability<[unknown]>; - DefaultNodeNullability: boolean extends PartialTypes['DefaultNodeNullability'] - ? false - : PartialTypes['DefaultNodeNullability'] & boolean; - } - export interface SchemaBuilder { - pageInfoRef: () => ObjectRef; - nodeInterfaceRef: () => InterfaceRef; - node: [], Param extends ObjectParam>( - param: Param, - options: NodeObjectOptions, - fields?: ObjectFieldsShape>, - ) => ObjectRef, ParentShape>; - globalConnectionFields: ( - fields: ObjectFieldsShape>, - ) => void; - globalConnectionField: ( - name: string, - field: ObjectFieldThunk>, - ) => void; - relayMutationField: < - Fields extends InputFieldMap, - Nullable extends boolean, - ResolveShape, - ResolveReturnShape, - Interfaces extends InterfaceParam[], - InputName extends string = 'input', - >( - name: string, - inputOptions: InputObjectRef | RelayMutationInputOptions, - fieldOptions: RelayMutationFieldOptions< - Types, - Fields, - Nullable, - InputName, - ResolveShape, - ResolveReturnShape - >, - payloadOptions: RelayMutationPayloadOptions, - ) => { - inputType: InputObjectRef>; - payloadType: ObjectRef; - }; - connectionObject: < - Type extends OutputType, - ResolveReturnShape, - EdgeNullability extends FieldNullability<[unknown]>, - NodeNullability extends boolean, - ConnectionInterfaces extends InterfaceParam[] = [], - EdgeInterfaces extends InterfaceParam[] = [], - >( - connectionOptions: ConnectionObjectOptions< - Types, - Type, - EdgeNullability, - NodeNullability, - ResolveReturnShape, - ConnectionInterfaces - > & { - name: string; - type: Type; - }, - ...args: NormalizeArgs< - [ - edgeOptions: - | ObjectRef<{ - cursor: string; - node?: ShapeFromTypeParam; - }> - | (ConnectionEdgeObjectOptions< - Types, - Type, - NodeNullability, - ResolveReturnShape, - EdgeInterfaces - > & { - name?: string; - }), - ] - > - ) => ObjectRef>; - edgeObject: < - Type extends OutputType, - ResolveReturnShape, - NodeNullability extends boolean = Types['DefaultNodeNullability'], - Interfaces extends InterfaceParam[] = [], - >( - edgeOptions: ConnectionEdgeObjectOptions< - Types, - Type, - NodeNullability, - ResolveReturnShape, - Interfaces - > & { - type: Type; - name: string; - nodeNullable?: NodeNullability; - }, - ) => ObjectRef<{ - cursor: string; - node: ShapeFromTypeParam; - }>; - } - export interface InputFieldBuilder< - Types extends SchemaTypes, - Kind extends 'Arg' | 'InputObject', - > { - connectionArgs: () => { - [K in keyof DefaultConnectionArguments]-?: InputFieldRef< - DefaultConnectionArguments[K], - Kind - >; - }; - globalID: >( - ...args: NormalizeArgs<[options: GlobalIDInputFieldOptions]> - ) => InputFieldRef< - InputShapeFromTypeParam< - Types, - GlobalIDInputShape ? T : string>, - Req - >, - Kind - >; - globalIDList: , For extends ObjectParam>( - ...args: NormalizeArgs<[options: GlobalIDListInputFieldOptions]> - ) => InputFieldRef< - InputShapeFromTypeParam< - Types, - [ - { - [inputShapeKey]: { - typename: string; - id: For extends NodeRef ? T : string; - }; - }, - ], - Req - >, - Kind - >; - } - export interface RootFieldBuilder< - Types extends SchemaTypes, - ParentShape, - Kind extends FieldKind = FieldKind, - > { - globalID: < - Args extends InputFieldMap, - Nullable extends FieldNullability<'ID'>, - ResolveReturnShape, - >( - options: GlobalIDFieldOptions, - ) => FieldRef>; - globalIDList: < - Args extends InputFieldMap, - Nullable extends FieldNullability<['ID']>, - ResolveReturnShape, - >( - options: GlobalIDListFieldOptions< - Types, - ParentShape, - Args, - Nullable, - ResolveReturnShape, - Kind - >, - ) => FieldRef>; - node: ( - options: NodeFieldOptions, - ) => FieldRef; - nodeList: ( - options: NodeListFieldOptions, - ) => FieldRef; - connection: < - Type extends OutputType, - Args extends InputFieldMap, - Nullable extends boolean, - ResolveShape, - ResolveReturnShape, - EdgeNullability extends FieldNullability<[unknown]> = Types['DefaultEdgesNullability'], - NodeNullability extends boolean = Types['DefaultNodeNullability'], - ConnectionInterfaces extends InterfaceParam[] = [], - EdgeInterfaces extends InterfaceParam[] = [], - >( - options: FieldOptionsFromKind< - Types, - ParentShape, - Type, - Nullable, - (InputFieldMap extends Args ? {} : Args) & - InputFieldsFromShape, - Kind, - ResolveShape, - ResolveReturnShape - > extends infer FieldOptions - ? ConnectionFieldOptions< - Types, - FieldOptions extends { + export namespace PothosSchemaTypes { + export interface Plugins { + relay: PothosRelayPlugin; + } + export interface SchemaBuilderOptions { + relayOptions: RelayPluginOptions; + } + export interface UserSchemaTypes { + Connection: {}; + DefaultEdgesNullability: FieldNullability<[ + unknown + ]>; + DefaultNodeNullability: boolean; + } + export interface ExtendDefaultTypes> { + Connection: PartialTypes["Connection"] & {}; + DefaultEdgesNullability: FieldNullability<[ + unknown + ]> extends PartialTypes["DefaultEdgesNullability"] ? DefaultEdgesNullability : PartialTypes["DefaultEdgesNullability"] & FieldNullability<[ + unknown + ]>; + DefaultNodeNullability: boolean extends PartialTypes["DefaultNodeNullability"] ? false : PartialTypes["DefaultNodeNullability"] & boolean; + } + export interface SchemaBuilder { + pageInfoRef: () => ObjectRef; + nodeInterfaceRef: () => InterfaceRef; + node: [], Param extends ObjectParam, IDShape = string>(param: Param, options: NodeObjectOptions, fields?: ObjectFieldsShape>) => NodeRef, ParentShape, IDShape>; + globalConnectionFields: (fields: ObjectFieldsShape>) => void; + globalConnectionField: (name: string, field: ObjectFieldThunk>) => void; + relayMutationField: [], InputName extends string = "input">(name: string, inputOptions: InputObjectRef | RelayMutationInputOptions, fieldOptions: RelayMutationFieldOptions, payloadOptions: RelayMutationPayloadOptions) => { + inputType: InputObjectRef>; + payloadType: ObjectRef; + }; + connectionObject: , ResolveReturnShape, EdgeNullability extends FieldNullability<[ + unknown + ]>, NodeNullability extends boolean, ConnectionInterfaces extends InterfaceParam[] = [ + ], EdgeInterfaces extends InterfaceParam[] = [ + ]>(connectionOptions: ConnectionObjectOptions & { + name: string; + type: Type; + }, ...args: NormalizeArgs<[ + edgeOptions: ObjectRef<{ + cursor: string; + node?: ShapeFromTypeParam; + }> | (ConnectionEdgeObjectOptions & { + name?: string; + }) + ]>) => ObjectRef>; + edgeObject: , ResolveReturnShape, NodeNullability extends boolean = Types["DefaultNodeNullability"], Interfaces extends InterfaceParam[] = [ + ]>(edgeOptions: ConnectionEdgeObjectOptions & { + type: Type; + name: string; + nodeNullable?: NodeNullability; + }) => ObjectRef<{ + cursor: string; + node: ShapeFromTypeParam; + }>; + } + export interface InputFieldBuilder { + connectionArgs: () => { + [K in keyof DefaultConnectionArguments]-?: InputFieldRef; + }; + globalID: >(...args: NormalizeArgs<[ + options: GlobalIDInputFieldOptions + ]>) => InputFieldRef ? T : string>, Req>, Kind>; + globalIDList: , For extends ObjectParam>(...args: NormalizeArgs<[ + options: GlobalIDListInputFieldOptions + ]>) => InputFieldRef ? T : string; + }; + } + ], Req>, Kind>; + } + export interface RootFieldBuilder { + globalID: , ResolveReturnShape>(options: GlobalIDFieldOptions) => FieldRef>; + globalIDList: , ResolveReturnShape>(options: GlobalIDListFieldOptions) => FieldRef>; + node: (options: NodeFieldOptions) => FieldRef; + nodeList: (options: NodeListFieldOptions) => FieldRef; + connection: , Args extends InputFieldMap, Nullable extends boolean, ResolveShape, ResolveReturnShape, EdgeNullability extends FieldNullability<[ + unknown + ]> = Types["DefaultEdgesNullability"], NodeNullability extends boolean = Types["DefaultNodeNullability"], ConnectionInterfaces extends InterfaceParam[] = [ + ], EdgeInterfaces extends InterfaceParam[] = [ + ]>(options: FieldOptionsFromKind, Kind, ResolveShape, ResolveReturnShape> extends infer FieldOptions ? ConnectionFieldOptions unknown; - } - ? P - : unknown extends ResolveShape - ? ParentShape - : ResolveShape, - Type, - Nullable, - EdgeNullability, - NodeNullability, - Args, - ResolveReturnShape - > & - Omit - : never, - ...args: NormalizeArgs< - [ - connectionOptions: - | ObjectRef< - ConnectionShapeForType - > - | Omit< - ConnectionObjectOptions< - Types, - Type, - EdgeNullability, - NodeNullability, - ResolveReturnShape, - ConnectionInterfaces - >, - 'edgesNullable' - >, - edgeOptions: - | ObjectRef<{ - cursor: string; - node?: ShapeFromTypeParam; - }> - | ConnectionEdgeObjectOptions< - Types, - Type, - NodeNullability, - ResolveReturnShape, - EdgeInterfaces - >, - ], - 0 - > - ) => FieldRef< - ConnectionShapeForType - >; - } - export interface ConnectionFieldOptions< - Types extends SchemaTypes, - ParentShape, - Type extends OutputType, - Nullable extends boolean, - EdgeNullability extends FieldNullability<[unknown]>, - NodeNullability extends boolean, - Args extends InputFieldMap, - ResolveReturnShape, - > { - type: Type; - args?: Args; - edgesNullable?: EdgeNullability; - nodeNullable?: NodeNullability; - resolve: Resolver< - ParentShape, - InputShapeFromFields & DefaultConnectionArguments, - Types['Context'], - ConnectionShapeForType, - ResolveReturnShape - >; - } - export interface ConnectionObjectOptions< - Types extends SchemaTypes, - Type extends OutputType, - EdgeNullability extends FieldNullability<[unknown]>, - NodeNullability extends boolean, - Resolved, - Interfaces extends InterfaceParam[] = [], - > extends ObjectTypeWithInterfaceOptions< - Types, - ConnectionShapeFromResolve, - Interfaces - > { - name?: string; - edgesNullable?: EdgeNullability; - nodeNullable?: NodeNullability; - edgesField?: Omit< - ObjectFieldOptions< - Types, - {}, - ObjectRef<{}>, - Types['DefaultNodeNullability'], - {}, - GlobalIDShape | string - >, - 'args' | 'nullable' | 'resolve' | 'type' - >; - } - export interface ConnectionEdgeObjectOptions< - Types extends SchemaTypes, - Type extends OutputType, - NodeNullability extends boolean, - Resolved, - Interfaces extends InterfaceParam[] = [], - > extends ObjectTypeWithInterfaceOptions< - Types, - NonNullable< - ConnectionShapeFromResolve['edges'] - >[number], - Interfaces - > { - name?: string; - nodeField?: Omit< - ObjectFieldOptions< - Types, - {}, - ObjectRef<{}>, - Types['DefaultNodeNullability'], - {}, - GlobalIDShape | string - >, - 'args' | 'nullable' | 'resolve' | 'type' - >; - } - export interface DefaultConnectionArguments { - first?: number | null | undefined; - last?: number | null | undefined; - before?: string | null | undefined; - after?: string | null | undefined; - } - export interface ConnectionShapeHelper { - shape: ConnectionShape; + } ? P : unknown extends ResolveShape ? ParentShape : ResolveShape, Type, Nullable, EdgeNullability, NodeNullability, Args, ResolveReturnShape> & Omit : never, ...args: NormalizeArgs<[ + connectionOptions: ObjectRef> | Omit, "edgesNullable">, + edgeOptions: ObjectRef<{ + cursor: string; + node?: ShapeFromTypeParam; + }> | ConnectionEdgeObjectOptions + ], 0>) => FieldRef>; + } + export interface ConnectionFieldOptions, Nullable extends boolean, EdgeNullability extends FieldNullability<[ + unknown + ]>, NodeNullability extends boolean, Args extends InputFieldMap, ResolveReturnShape> { + type: Type; + args?: Args; + edgesNullable?: EdgeNullability; + nodeNullable?: NodeNullability; + resolve: Resolver & DefaultConnectionArguments, Types["Context"], ConnectionShapeForType, ResolveReturnShape>; + } + export interface ConnectionObjectOptions, EdgeNullability extends FieldNullability<[ + unknown + ]>, NodeNullability extends boolean, Resolved, Interfaces extends InterfaceParam[] = [ + ]> extends ObjectTypeWithInterfaceOptions, Interfaces> { + name?: string; + edgesNullable?: EdgeNullability; + nodeNullable?: NodeNullability; + edgesField?: Omit, Types["DefaultNodeNullability"], {}, GlobalIDShape | string>, "args" | "nullable" | "resolve" | "type">; + } + export interface ConnectionEdgeObjectOptions, NodeNullability extends boolean, Resolved, Interfaces extends InterfaceParam[] = [ + ]> extends ObjectTypeWithInterfaceOptions["edges"]>[number], Interfaces> { + name?: string; + nodeField?: Omit, Types["DefaultNodeNullability"], {}, GlobalIDShape | string>, "args" | "nullable" | "resolve" | "type">; + } + export interface DefaultConnectionArguments { + first?: number | null | undefined; + last?: number | null | undefined; + before?: string | null | undefined; + after?: string | null | undefined; + } + export interface ConnectionShapeHelper { + shape: ConnectionShape; + } } - } } diff --git a/packages/deno/packages/plugin-relay/index.ts b/packages/deno/packages/plugin-relay/index.ts index 650234add..e03f3e3ca 100644 --- a/packages/deno/packages/plugin-relay/index.ts +++ b/packages/deno/packages/plugin-relay/index.ts @@ -3,73 +3,40 @@ import './global-types.ts'; import './field-builder.ts'; import './input-field-builder.ts'; import './schema-builder.ts'; -import { GraphQLFieldResolver } from 'https://cdn.skypack.dev/graphql?dts'; -import SchemaBuilder, { - BasePlugin, - createInputValueMapper, - mapInputFields, - PothosOutputFieldConfig, - SchemaTypes, -} from '../core/index.ts'; +import { GraphQLFieldResolver, GraphQLResolveInfo } from 'https://cdn.skypack.dev/graphql?dts'; +import SchemaBuilder, { BasePlugin, createInputValueMapper, mapInputFields, PothosOutputFieldConfig, SchemaTypes, } from '../core/index.ts'; import { internalDecodeGlobalID } from './utils/internal.ts'; +export * from './node-ref.ts'; export * from './types.ts'; export * from './utils/index.ts'; -const pluginName = 'relay'; +const pluginName = "relay"; export default pluginName; export class PothosRelayPlugin extends BasePlugin { - override wrapResolve( - resolver: GraphQLFieldResolver, - fieldConfig: PothosOutputFieldConfig, - ): GraphQLFieldResolver { - const argMappings = mapInputFields(fieldConfig.args, this.buildCache, (inputField) => { - if (inputField.extensions?.isRelayGlobalID) { - return true; - } - return null; - }); - if (!argMappings) { - return resolver; + override wrapResolve(resolver: GraphQLFieldResolver, fieldConfig: PothosOutputFieldConfig): GraphQLFieldResolver { + const argMappings = mapInputFields(fieldConfig.args, this.buildCache, (inputField) => { + if (inputField.extensions?.isRelayGlobalID) { + return (inputField.extensions?.relayGlobalIDFor ?? true) as true | string[]; + } + return null; + }); + if (!argMappings) { + return resolver; + } + const argMapper = createInputValueMapper(argMappings, (globalID, mappings, ctx: Types["Context"], info: GraphQLResolveInfo) => internalDecodeGlobalID(this.builder, String(globalID), ctx, info, Array.isArray(mappings.value) ? mappings.value : false)); + return (parent, args, context, info) => resolver(parent, argMapper(args, undefined, context, info), context, info); } - const argMapper = createInputValueMapper( - argMappings, - (globalID, mappings, ctx: Types['Context'], info: GraphQLResolveInfo) => - internalDecodeGlobalID( - this.builder, - String(globalID), - ctx, - info, - Array.isArray(mappings.value) ? mappings.value : false, - ), - ); - return (parent, args, context, info) => - resolver(parent, argMapper(args, undefined, context, info), context, info); - } - override wrapSubscribe( - subscribe: GraphQLFieldResolver | undefined, - fieldConfig: PothosOutputFieldConfig, - ): GraphQLFieldResolver | undefined { - const argMappings = mapInputFields(fieldConfig.args, this.buildCache, (inputField) => { - if (inputField.extensions?.isRelayGlobalID) { - return true; - } - return null; - }); - if (!argMappings || !subscribe) { - return subscribe; + override wrapSubscribe(subscribe: GraphQLFieldResolver | undefined, fieldConfig: PothosOutputFieldConfig): GraphQLFieldResolver | undefined { + const argMappings = mapInputFields(fieldConfig.args, this.buildCache, (inputField) => { + if (inputField.extensions?.isRelayGlobalID) { + return (inputField.extensions?.relayGlobalIDFor ?? true) as true | string[]; + } + return null; + }); + if (!argMappings || !subscribe) { + return subscribe; + } + const argMapper = createInputValueMapper(argMappings, (globalID, mappings, ctx: Types["Context"], info: GraphQLResolveInfo) => internalDecodeGlobalID(this.builder, String(globalID), ctx, info, Array.isArray(mappings.value) ? mappings.value : false)); + return (parent, args, context, info) => subscribe(parent, argMapper(args, undefined, context, info), context, info); } - const argMapper = createInputValueMapper( - argMappings, - (globalID, mappings, ctx: Types['Context'], info: GraphQLResolveInfo) => - internalDecodeGlobalID( - this.builder, - String(globalID), - ctx, - info, - Array.isArray(mappings.value) ? mappings.value : false, - ), - ); - return (parent, args, context, info) => - subscribe(parent, argMapper(args, undefined, context, info), context, info); - } } SchemaBuilder.registerPlugin(pluginName, PothosRelayPlugin); diff --git a/packages/deno/packages/plugin-relay/input-field-builder.ts b/packages/deno/packages/plugin-relay/input-field-builder.ts index 7ff3fca8c..ae054259e 100644 --- a/packages/deno/packages/plugin-relay/input-field-builder.ts +++ b/packages/deno/packages/plugin-relay/input-field-builder.ts @@ -1,83 +1,40 @@ // @ts-nocheck -import { - FieldRequiredness, - InputFieldBuilder, - InputFieldRef, - InputShapeFromTypeParam, - ObjectRef, - SchemaTypes, -} from '../core/index.ts'; -import { - GlobalIDInputFieldOptions, - GlobalIDInputShape, - GlobalIDListInputFieldOptions, -} from './types.ts'; +import { FieldRequiredness, InputFieldBuilder, InputFieldRef, InputShapeFromTypeParam, ObjectRef, SchemaTypes, } from '../core/index.ts'; +import { GlobalIDInputFieldOptions, GlobalIDInputShape, GlobalIDListInputFieldOptions, } from './types.ts'; type DefaultSchemaTypes = PothosSchemaTypes.ExtendDefaultTypes<{}>; -const inputFieldBuilder = InputFieldBuilder.prototype as PothosSchemaTypes.InputFieldBuilder< - DefaultSchemaTypes, - 'Arg' | 'InputObject' ->; -inputFieldBuilder.globalIDList = function globalIDList>( - { - for: forTypes, - ...options - }: GlobalIDListInputFieldOptions = {} as never, -) { - return this.idList({ - ...options, - extensions: { - ...options.extensions, - isRelayGlobalID: true, - relayGlobalIDFor: - ( - (forTypes && - (Array.isArray(forTypes) ? forTypes : [forTypes])) as ObjectRef[] - )?.map( - (type: ObjectRef) => this.builder.configStore.getTypeConfig(type).name, - ) ?? null, - }, - }) as never; +const inputFieldBuilder = InputFieldBuilder.prototype as PothosSchemaTypes.InputFieldBuilder; +inputFieldBuilder.globalIDList = function globalIDList>({ for: forTypes, ...options }: GlobalIDListInputFieldOptions = {} as never) { + return this.idList({ + ...options, + extensions: { + ...options.extensions, + isRelayGlobalID: true, + relayGlobalIDFor: ((forTypes && + (Array.isArray(forTypes) ? forTypes : [forTypes])) as ObjectRef[])?.map((type: ObjectRef) => this.builder.configStore.getTypeConfig(type).name) ?? null, + }, + }) as never; }; -inputFieldBuilder.globalID = function globalID( - { - for: forTypes, - ...options - }: GlobalIDInputFieldOptions = {} as never, -) { - return this.id({ - ...options, - extensions: { - ...options.extensions, - isRelayGlobalID: true, - relayGlobalIDFor: - ( - (forTypes && - (Array.isArray(forTypes) ? forTypes : [forTypes])) as ObjectRef[] - )?.map( - (type: ObjectRef) => this.builder.configStore.getTypeConfig(type).name, - ) ?? null, - }, - }) as unknown as InputFieldRef< - InputShapeFromTypeParam - > as never; +inputFieldBuilder.globalID = function globalID({ for: forTypes, ...options }: GlobalIDInputFieldOptions = {} as never) { + return this.id({ + ...options, + extensions: { + ...options.extensions, + isRelayGlobalID: true, + relayGlobalIDFor: ((forTypes && + (Array.isArray(forTypes) ? forTypes : [forTypes])) as ObjectRef[])?.map((type: ObjectRef) => this.builder.configStore.getTypeConfig(type).name) ?? null, + }, + }) as unknown as InputFieldRef> as never; }; inputFieldBuilder.connectionArgs = function connectionArgs() { - const { + const { // TODO(breaking) make this default match other cursor fields - cursorType = 'ID', - beforeArgOptions = {} as never, - afterArgOptions = {} as never, - firstArgOptions = {} as never, - lastArgOptions = {} as never, - } = this.builder.options.relayOptions; - return { - before: this.field({ ...beforeArgOptions, type: cursorType, required: false }) as InputFieldRef< - string | null - >, - after: this.field({ ...afterArgOptions, type: cursorType, required: false }) as InputFieldRef< - string | null - >, - first: this.int({ ...firstArgOptions, required: false }), - last: this.int({ ...lastArgOptions, required: false }), - }; + cursorType = "ID", beforeArgOptions = {} as never, afterArgOptions = {} as never, firstArgOptions = {} as never, lastArgOptions = {} as never, } = this.builder.options.relayOptions; + return { + before: this.field({ ...beforeArgOptions, type: cursorType, required: false }) as InputFieldRef, + after: this.field({ ...afterArgOptions, type: cursorType, required: false }) as InputFieldRef, + first: this.int({ ...firstArgOptions, required: false }), + last: this.int({ ...lastArgOptions, required: false }), + }; }; diff --git a/packages/deno/packages/plugin-relay/node-ref.ts b/packages/deno/packages/plugin-relay/node-ref.ts new file mode 100644 index 000000000..390897935 --- /dev/null +++ b/packages/deno/packages/plugin-relay/node-ref.ts @@ -0,0 +1,6 @@ +// @ts-nocheck +import { ObjectRef } from '../core/index.ts'; +export const relayIDShapeKey = Symbol.for("Pothos.relayIDShapeKey"); +export class NodeRef extends ObjectRef { + [relayIDShapeKey]!: K; +} diff --git a/packages/deno/packages/plugin-relay/schema-builder.ts b/packages/deno/packages/plugin-relay/schema-builder.ts index 2d516f683..9e3fd503b 100644 --- a/packages/deno/packages/plugin-relay/schema-builder.ts +++ b/packages/deno/packages/plugin-relay/schema-builder.ts @@ -153,7 +153,7 @@ schemaBuilderProto.nodeInterfaceRef = function nodeInterfaceRef() { } return ref; }; -schemaBuilderProto.node = function node(param, { interfaces, ...options }, fields) { +schemaBuilderProto.node = function node(param, { interfaces, extensions, id, ...options }, fields) { verifyRef(param); const interfacesWithNode: () => InterfaceParam[] = () => [ this.nodeInterfaceRef(), @@ -163,6 +163,10 @@ schemaBuilderProto.node = function node(param, { interfaces, ...options }, field const ref = this.objectType<[ ], ObjectParam>(param, { ...(options as {}), + extensions: { + ...extensions, + pothosParseGlobalID: id.parse, + }, isTypeOf: options.isTypeOf ?? (typeof param === "function" ? (maybeNode: unknown, context: object, info: GraphQLResolveInfo) => { @@ -195,16 +199,16 @@ schemaBuilderProto.node = function node(param, { interfaces, ...options }, field this.objectField(ref, this.options.relayOptions.idFieldName ?? "id", (t) => t.globalID<{}, false, Promise>>({ nullable: false, ...this.options.relayOptions.idFieldOptions, - ...options.id, + ...id, args: {}, resolve: async (parent, args, context, info) => ({ type: nodeConfig.name, // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - id: await options.id.resolve(parent, args, context, info), + id: await id.resolve(parent, args, context, info), }), })); }); - return ref; + return ref as never; }; schemaBuilderProto.globalConnectionField = function globalConnectionField(name, field) { const onRef = (ref: ObjectRef>) => { diff --git a/packages/deno/packages/plugin-relay/types.ts b/packages/deno/packages/plugin-relay/types.ts index e3d8a2507..d80f1cbd7 100644 --- a/packages/deno/packages/plugin-relay/types.ts +++ b/packages/deno/packages/plugin-relay/types.ts @@ -118,23 +118,29 @@ export type ConnectionShapeFromResolve, Interfaces extends InterfaceParam[]> = ObjectTypeOptions, Interfaces>; -export type NodeObjectOptions, Interfaces extends InterfaceParam[]> = NodeBaseObjectOptionsForParam & { - id: Omit, "ID", false, {}, "Object", OutputShape, MaybePromise>>, "args" | "nullable" | "type">; +export type NodeObjectOptions, Interfaces extends InterfaceParam[], IDShape = string> = NodeBaseObjectOptionsForParam & { + id: Omit, "ID", false, {}, "Object", OutputShape, MaybePromise>>, "args" | "nullable" | "type"> & { + parse?: (id: string, ctx: Types["Context"]) => IDShape; + }; brandLoadedObjects?: boolean; - loadOne?: (id: string, context: Types["Context"]) => MaybePromise | null | undefined>; - loadMany?: (ids: string[], context: Types["Context"]) => MaybePromise | null | undefined>[]>; - loadWithoutCache?: (id: string, context: Types["Context"], info: GraphQLResolveInfo) => MaybePromise | null | undefined>; - loadManyWithoutCache?: (ids: string[], context: Types["Context"]) => MaybePromise | null | undefined>[]>; + loadOne?: (id: IDShape, context: Types["Context"]) => MaybePromise | null | undefined>; + loadMany?: (ids: IDShape[], context: Types["Context"]) => MaybePromise | null | undefined>[]>; + loadWithoutCache?: (id: IDShape, context: Types["Context"], info: GraphQLResolveInfo) => MaybePromise | null | undefined>; + loadManyWithoutCache?: (ids: IDShape[], context: Types["Context"]) => MaybePromise | null | undefined>[]>; }; export type GlobalIDFieldOptions = Omit, "resolve" | "type"> & { resolve: Resolver, Types["Context"], ShapeFromTypeParam | string>, true>, ResolveReturnShape>; }; -export type GlobalIDInputFieldOptions = Omit[Kind], "type">; +export type GlobalIDInputFieldOptions = Omit[Kind], "type"> & { + for?: For | For[]; +}; export type GlobalIDListInputFieldOptions, Kind extends "Arg" | "InputObject"> = Omit, Kind extends "Arg" | "InputObject", For = unknown> = Omit[Kind], "type">; +], Req>[Kind], "type"> & { + for?: For | For[]; +}; export type NodeIDFieldOptions = Omit, "resolve" | "type"> & { resolve: Resolver, Types["Context"], ShapeFromTypeParam | string>, true>, ResolveReturnShape>; }; @@ -166,10 +172,10 @@ export type NodeListFieldOptions, ResolveReturnShape>; }; -export interface GlobalIDInputShape { +export interface GlobalIDInputShape { [inputShapeKey]: { typename: string; - id: string; + id: T; }; } export type RelayMutationInputOptions = Omit, "fields"> & { diff --git a/packages/deno/packages/plugin-relay/utils/internal.ts b/packages/deno/packages/plugin-relay/utils/internal.ts index 7fa7c8cac..8f9e03355 100644 --- a/packages/deno/packages/plugin-relay/utils/internal.ts +++ b/packages/deno/packages/plugin-relay/utils/internal.ts @@ -1,4 +1,5 @@ // @ts-nocheck +import { GraphQLResolveInfo } from 'https://cdn.skypack.dev/graphql?dts'; import { SchemaTypes } from '../../core/index.ts'; import { decodeGlobalID, encodeGlobalID } from './global-ids.ts'; export function internalEncodeGlobalID(builder: PothosSchemaTypes.SchemaBuilder, typename: string, id: bigint | number | string, ctx: object) { @@ -7,19 +8,12 @@ export function internalEncodeGlobalID(builder: Potho } return encodeGlobalID(typename, id); } -<<<<<<< HEAD -export function internalDecodeGlobalID(builder: PothosSchemaTypes.SchemaBuilder, globalID: string, ctx: object) { - if (builder.options.relayOptions.decodeGlobalID) { - return builder.options.relayOptions.decodeGlobalID(globalID, ctx); - } - return decodeGlobalID(globalID); -======= export function internalDecodeGlobalID(builder: PothosSchemaTypes.SchemaBuilder, globalID: string, ctx: object, info: GraphQLResolveInfo, parseIdsForTypes: boolean | string[]) { const decoded = builder.options.relayOptions.decodeGlobalID ? builder.options.relayOptions.decodeGlobalID(globalID, ctx) : decodeGlobalID(globalID); if (Array.isArray(parseIdsForTypes) && !parseIdsForTypes.includes(decoded.typename)) { - throw new PothosValidationError(`ID: ${globalID} is not of type: ${parseIdsForTypes.join(", ")}`); + throw new Error(`ID: ${globalID} is not of type: ${parseIdsForTypes.join(", ")}`); } if (parseIdsForTypes !== false) { const parseID = info.schema.getType(decoded.typename)?.extensions?.pothosParseGlobalID as (id: string, ctx: object) => string; @@ -31,5 +25,4 @@ export function internalDecodeGlobalID(builder: Potho } } return decoded; ->>>>>>> ec530aba (Handle non-NodeRef parameters in 'for' and only decode when 'for' is present) } diff --git a/packages/plugin-relay/src/utils/internal.ts b/packages/plugin-relay/src/utils/internal.ts index 3fc9f65bb..cb1e32cd3 100644 --- a/packages/plugin-relay/src/utils/internal.ts +++ b/packages/plugin-relay/src/utils/internal.ts @@ -26,15 +26,8 @@ export function internalDecodeGlobalID( ? builder.options.relayOptions.decodeGlobalID(globalID, ctx) : decodeGlobalID(globalID); -<<<<<<< HEAD - if (types && !types.includes(decoded.typename)) { - throw new Error(`ID: ${globalID} is not of type: ${types.join(', ')}`); -======= if (Array.isArray(parseIdsForTypes) && !parseIdsForTypes.includes(decoded.typename)) { - throw new PothosValidationError( - `ID: ${globalID} is not of type: ${parseIdsForTypes.join(', ')}`, - ); ->>>>>>> ec530aba (Handle non-NodeRef parameters in 'for' and only decode when 'for' is present) + throw new Error(`ID: ${globalID} is not of type: ${parseIdsForTypes.join(', ')}`); } if (parseIdsForTypes !== false) {