From 1342a602fb3607d58faeb1ac556854ed311c9458 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Wed, 14 Feb 2024 22:33:48 -0500 Subject: [PATCH 01/71] feat(builder): introduce an experimental builder api --- examples/schema.graphql | 1247 ++++++++++++++++++++++++++++++++++++++ src/demo.ts | 1233 +++++++++++++++++++++++++++++++++++++ src/lib/generateTypes.ts | 249 ++++++++ 3 files changed, 2729 insertions(+) create mode 100644 examples/schema.graphql create mode 100644 src/demo.ts create mode 100644 src/lib/generateTypes.ts diff --git a/examples/schema.graphql b/examples/schema.graphql new file mode 100644 index 000000000..bcb30461c --- /dev/null +++ b/examples/schema.graphql @@ -0,0 +1,1247 @@ +union AccelerateStatus = AccelerateStatusDisabled | AccelerateStatusEnabled + +type AccelerateStatusDisabled implements ProductStatus { + enabled: Boolean! +} + +type AccelerateStatusEnabled implements ProductStatus { + enabled: Boolean! +} + +type Count { + number: Int! +} + +""" +ISO 3166-1 alpha-2 country code +""" +enum CountryCode { + AD + AE + AF + AG + AI + AL + AM + AO + AQ + AR + AS + AT + AU + AW + AX + AZ + BA + BB + BD + BE + BF + BG + BH + BI + BJ + BL + BM + BN + BO + BQ + BR + BS + BT + BV + BW + BY + BZ + CA + CC + CD + CF + CG + CH + CI + CK + CL + CM + CN + CO + CR + CU + CV + CW + CX + CY + CZ + DE + DJ + DK + DM + DO + DZ + EC + EE + EG + EH + ER + ES + ET + FI + FJ + FK + FM + FO + FR + GA + GB + GD + GE + GF + GG + GH + GI + GL + GM + GN + GP + GQ + GR + GS + GT + GU + GW + GY + HK + HM + HN + HR + HT + HU + ID + IE + IL + IM + IN + IO + IQ + IR + IS + IT + JE + JM + JO + JP + KE + KG + KH + KI + KM + KN + KP + KR + KW + KY + KZ + LA + LB + LC + LI + LK + LR + LS + LT + LU + LV + LY + MA + MC + MD + ME + MF + MG + MH + MK + ML + MM + MN + MO + MP + MQ + MR + MS + MT + MU + MV + MW + MX + MY + MZ + NA + NC + NE + NF + NG + NI + NL + NO + NP + NR + NU + NZ + OM + PA + PE + PF + PG + PH + PK + PL + PM + PN + PR + PS + PT + PW + PY + QA + RE + RO + RS + RU + RW + SA + SB + SC + SD + SE + SG + SH + SI + SJ + SK + SL + SM + SN + SO + SR + SS + ST + SV + SX + SY + SZ + TC + TD + TF + TG + TH + TJ + TK + TL + TM + TN + TO + TR + TT + TV + TW + TZ + UA + UG + UM + US + UY + UZ + VA + VC + VE + VG + VI + VN + VU + WF + WS + YE + YT + ZA + ZM + ZW +} + +type DatabaseLink { + connectionStringHint: String! + id: ID! + protocol: String! + region: String +} + +type DatabaseLinkNode implements Node { + connectionStringHint: ID! + displayName: String! + id: String! +} + +""" +A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.This scalar is serialized to a string in ISO 8601 format and parsed from a string in ISO 8601 format. +""" +scalar Date + +type Environment { + accelerate: EnvironmentAccelerate! + createdAt: Date! + displayName: String! + id: ID! + isDefault: Boolean! + project: Project! + pulse: EnvironmentPulse! + serviceKeys: [ServiceKey!]! + tenantId: ID! +} + +type EnvironmentAccelerate { + databaseLink: DatabaseLink + holds: [ProductHold!]! + status: AccelerateStatus! + usage(timeWindow: EnvironmentAccelerateUsageTimeWindowInput!): EnvironmentAccelerateUsage! +} + +type EnvironmentAccelerateTimeSeriesPoints { + queries: EnvironmentAccelerateUsageTimeSeriesPointsQueries! + timestamps: [Date!]! +} + +type EnvironmentAccelerateUsage { + latency: EnvironmentAccelerateUsageLatency! + overview: EnvironmentAccelerateUsageOverview! + timeInterval: TimeInterval! + timeSeries: EnvironmentAccelerateUsageTimeSeries! +} + +type EnvironmentAccelerateUsageLatency { + queries: EnvironmentAccelerateUsageLatencyQueries! +} + +type EnvironmentAccelerateUsageLatencyQueries { + cached: EnvironmentAccelerateUsageLatencyQuery! + origin: EnvironmentAccelerateUsageLatencyQuery! +} + +type EnvironmentAccelerateUsageLatencyQuery { + count: Count! + durationAverage: MetricValue! + durationPercentiles: [Percentile!]! +} + +type EnvironmentAccelerateUsageOverview { + egress: EnvironmentAccelerateUsageOverviewEgress! + queries: EnvironmentAccelerateUsageOverviewQueries! +} + +type EnvironmentAccelerateUsageOverviewCacheHit { + ratioToMiss: MetricValue! +} + +type EnvironmentAccelerateUsageOverviewEgress { + averageResponseSize: StorageValue! + requestsServedFromOrigin: Count! + total: StorageValue! +} + +type EnvironmentAccelerateUsageOverviewQueries { + cacheHit: EnvironmentAccelerateUsageOverviewCacheHit! + cacheableCount: Count! + totalCount: Count! +} + +type EnvironmentAccelerateUsageTimeSeries { + points: EnvironmentAccelerateTimeSeriesPoints! +} + +type EnvironmentAccelerateUsageTimeSeriesPointsQueries { + miss: [EnvironmentAccelerateUsageTimeSeriesPointsQuery!] + none: [EnvironmentAccelerateUsageTimeSeriesPointsQuery!] + swr: [EnvironmentAccelerateUsageTimeSeriesPointsQuery!] + ttl: [EnvironmentAccelerateUsageTimeSeriesPointsQuery!] +} + +type EnvironmentAccelerateUsageTimeSeriesPointsQuery { + count: Count! + timestamp: Date! +} + +enum EnvironmentAccelerateUsageTimeWindowInput { + last6h + last7d + last24h + last30d + last30m +} + +type EnvironmentPulse { + databaseLink: DatabaseLink + status: PulseStatus! +} + +interface Error { + message: String! +} + +type ErrorInternal implements Error { + message: String! +} + +type ErrorUser implements Error { + message: String! +} + +type ErrorUserBusinessDeleteWorkspaceOnPaidPlan implements Error { + context: ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext! + message: String! +} + +type ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext { + plan: Plan! +} + +type ErrorUserBusinessNotAuthorized implements Error { + message: String! +} + +type ErrorUserBusinessPlanLimitHit implements Error { + context: ErrorUserBusinessPlanLimitHitContext! + message: String! +} + +type ErrorUserBusinessPlanLimitHitContext { + featureHandle: String +} + +type ErrorUserBusinessResourceNotFound implements Error { + context: ErrorUserBusinessResourceNotFoundContext! + message: String! +} + +type ErrorUserBusinessResourceNotFoundContext { + id: ID + typeName: ResourceType! +} + +type ErrorUserBusinessUserAlreadyMemberOfOrganization implements Error { + context: ErrorUserBusinessUserAlreadyMemberOfOrganizationContext! + message: String! +} + +type ErrorUserBusinessUserAlreadyMemberOfOrganizationContext { + user: User! + workspace: Workspace! +} + +type ErrorUserInput implements Error { + message: String! +} + +interface Feature { + displayName: String + handle: FeatureHandle! + id: ID! + stripeProductId: String! +} + +type FeatureAbstract implements Feature { + displayName: String + handle: FeatureHandle! + id: ID! + stripeProductId: String! +} + +enum FeatureHandle { + accelerateEgress + acceleratePurgeCache + accelerateQuery + access + createProject + organizationRole + platformSupport +} + +type FeatureResourceAggregation implements Feature { + displayName: String + handle: FeatureHandle! + id: ID! + resource: String! + scope: String! + stripeProductId: String! + valueResolver: FeatureResourceAggregationValueResolver! +} + +type FeatureResourceAggregationValueResolver { + type: FeatureResourceAggregationValueResolverType! +} + +enum FeatureResourceAggregationValueResolverType { + count +} + +type FeatureResourceProperty implements Feature { + displayName: String + handle: FeatureHandle! + id: ID! + resource: String! + scope: String! + stripeProductId: String! + valueResolver: FeatureResourcePropertyValueResolver! +} + +type FeatureResourcePropertyValueResolver { + field: String! + type: FeatureValueType! +} + +type FeatureValue implements Feature { + displayName: String + handle: FeatureHandle! + id: ID! + stripeProductId: String! + valueType: FeatureValueType! +} + +union FeatureValueType = + | FeatureValueTypeBoolean + | FeatureValueTypeEnum + | FeatureValueTypeNumber + | FeatureValueTypeString + +type FeatureValueTypeBoolean { + displayName: String! +} + +type FeatureValueTypeEnum { + displayName: String! + members: [FeatureValueTypeEnumMember!]! +} + +type FeatureValueTypeEnumMember { + description: String + value: String! +} + +type FeatureValueTypeNumber { + displayName: String! +} + +type FeatureValueTypeString { + displayName: String! +} + +union Limit = LimitEnum | LimitNumber + +type LimitEnum { + allowed: [String!]! +} + +type LimitNumber { + amount: Int! + type: NumberPredicateFnType! +} + +type Me { + user: User! + workspaces(orderBy: WorkspaceOrderBy = {}): [Workspace!]! +} + +enum MetricUnit { + ms + percent +} + +type MetricValue { + number: Float! + unit: MetricUnit +} + +type Mutation { + accelerateCachePurge(input: MutationAccelerateCachePurgeInput!): MutationAccelerateCachePurgeResult! + accelerateDisable(input: MutationAccelerateDisableInput!): MutationAccelerateDisableResult! + accelerateEnable(input: MutationAccelerateEnableInput!): MutationAccelerateEnableResult! + databaseLinkCreate(input: MutationDatabaseLinkCreateInput!): MutationDatabaseLinkCreateResult! + databaseLinkDelete(input: MutationDatabaseLinkDeleteInput!): MutationDatabaseLinkDeleteResult! + databaseLinkUpdate(input: MutationDatabaseLinkUpdateInput!): MutationDatabaseLinkUpdateResult! + environmentCreate(input: MutationEnvironmentCreateInput!): MutationEnvironmentCreateResult! + environmentDelete(input: MutationEnvironmentDeleteInput!): MutationEnvironmentDeleteResult! + environmentUpdate(input: MutationEnvironmentUpdateInput!): MutationEnvironmentUpdateResult! + projectCreate(input: MutationProjectCreateInput!): MutationProjectCreateResult! + projectDelete(input: MutationProjectDeleteInput!): MutationProjectDeleteResult! + projectUpdate(input: MutationProjectUpdateInput!): MutationProjectUpdateResult! + pulseDisable(input: MutationPulseDisableInput!): MutationPulseDisableResult! + pulseEnable(input: MutationPulseEnableInput!): MutationPulseEnableResult! + serviceKeyCreate(input: MutationServiceKeyCreateInput!): MutationServiceKeyCreateResult! + serviceKeyDelete(input: MutationServiceKeyDeleteInput!): MutationServiceKeyDeleteResult! + userUpdate(input: MutationUserUpdateInput!): MutationUserUpdateResult! + userUpdateDefaultWorkspace( + input: MutationUserUpdateDefaultWorkspaceInput! + ): MutationUserUpdateDefaultWorkspaceResult! + workspaceCreate(input: MutationWorkspaceCreateInput!): MutationWorkspaceCreateResult! + workspaceDelete(input: MutationWorkspaceDeleteInput!): MutationWorkspaceDeleteResult! + workspaceMembershipCreate( + input: MutationWorkspaceMembershipCreateInput! + ): MutationWorkspaceMembershipCreateResult! + workspaceMembershipDelete( + input: MutationWorkspaceMembershipDeleteInput! + ): MutationWorkspaceMembershipDeleteResult! + workspacePlanSubscriptionChange( + input: MutationWorkspacePlanSubscriptionChangeInput! + ): MutationWorkspacePlanSubscriptionChangeResult! + workspaceUpdate(input: MutationWorkspaceUpdateInput!): MutationWorkspaceUpdateResult! + workspaceUpdateBillingAddress( + input: MutationWorkspaceUpdateBillingAddressInput! + ): MutationWorkspaceUpdateBillingAddressResult! + workspaceUpdateBillingEmail( + input: MutationWorkspaceUpdateBillingEmailInput! + ): MutationWorkspaceUpdateBillingEmailResult! +} + +input MutationAccelerateCachePurgeInput { + environmentId: ID! +} + +union MutationAccelerateCachePurgeResult = ErrorInternal | SideEffectConfirmation + +input MutationAccelerateDisableInput { + environmentId: ID! +} + +union MutationAccelerateDisableResult = ErrorInternal | SideEffectConfirmation + +input MutationAccelerateEnableInput { + databaseLinkId: ID! +} + +union MutationAccelerateEnableResult = ErrorInternal | SideEffectConfirmation + +input MutationDatabaseLinkCreateInput { + connectionString: String! + displayName: String + environmentId: ID! + regionId: String +} + +union MutationDatabaseLinkCreateResult = DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound + +input MutationDatabaseLinkDeleteInput { + id: ID! +} + +union MutationDatabaseLinkDeleteResult = DatabaseLinkNode | ErrorInternal | ErrorUserBusinessResourceNotFound + +input MutationDatabaseLinkUpdateInput { + connectionString: String! + id: ID! + regionId: String! +} + +union MutationDatabaseLinkUpdateResult = DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound + +input MutationEnvironmentCreateInput { + displayName: String + isDefault: Boolean + projectId: ID! +} + +union MutationEnvironmentCreateResult = + | Environment + | ErrorInternal + | ErrorUserBusinessPlanLimitHit + | ErrorUserBusinessResourceNotFound + +input MutationEnvironmentDeleteInput { + id: ID! +} + +union MutationEnvironmentDeleteResult = Environment | ErrorInternal | ErrorUserBusinessResourceNotFound + +input MutationEnvironmentUpdateInput { + displayName: String + id: ID! + isDefault: Boolean +} + +union MutationEnvironmentUpdateResult = Environment | ErrorInternal | ErrorUserBusinessResourceNotFound + +input MutationProjectCreateInput { + displayName: String + workspaceId: ID! +} + +union MutationProjectCreateResult = + | ErrorInternal + | ErrorUserBusinessPlanLimitHit + | ErrorUserBusinessResourceNotFound + | Project + +input MutationProjectDeleteInput { + id: ID! +} + +union MutationProjectDeleteResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ProjectNode + +input MutationProjectUpdateInput { + displayName: String + id: ID! +} + +union MutationProjectUpdateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | Project + +input MutationPulseDisableInput { + environmentId: String! +} + +union MutationPulseDisableResult = ErrorInternal | ErrorUserBusinessResourceNotFound | SideEffectConfirmation + +input MutationPulseEnableInput { + databaseLinkId: String! +} + +union MutationPulseEnableResult = ErrorInternal | ErrorUser | SideEffectConfirmation + +input MutationServiceKeyCreateInput { + displayName: String + environmentId: ID! +} + +union MutationServiceKeyCreateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyWithValue + +input MutationServiceKeyDeleteInput { + id: String! +} + +union MutationServiceKeyDeleteResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyNode + +input MutationUserUpdateDefaultWorkspaceInput { + workspaceId: ID! +} + +union MutationUserUpdateDefaultWorkspaceResult = ErrorInternal | ErrorUserBusinessResourceNotFound | User + +input MutationUserUpdateInput { + displayName: String + id: ID! +} + +union MutationUserUpdateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | User + +input MutationWorkspaceCreateInput { + displayName: String +} + +union MutationWorkspaceCreateResult = ErrorInternal | Workspace + +input MutationWorkspaceDeleteInput { + id: ID! +} + +union MutationWorkspaceDeleteResult = + | ErrorInternal + | ErrorUserBusinessDeleteWorkspaceOnPaidPlan + | ErrorUserBusinessResourceNotFound + | WorkspaceNode + +input MutationWorkspaceMembershipCreateInput { + email: String! + role: WorkspaceRole! + workspaceId: ID! +} + +union MutationWorkspaceMembershipCreateResult = + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | ErrorUserBusinessUserAlreadyMemberOfOrganization + | WorkspaceMembership + +input MutationWorkspaceMembershipDeleteInput { + id: ID! +} + +union MutationWorkspaceMembershipDeleteResult = + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | WorkspaceMembershipNode + +input MutationWorkspacePlanSubscriptionChangeInput { + targetPlanId: ID! + workspaceId: ID! +} + +union MutationWorkspacePlanSubscriptionChangeResult = + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | PlanSubscription + +input MutationWorkspaceUpdateBillingAddressInput { + address: PhysicalAddressInput! + id: ID! +} + +union MutationWorkspaceUpdateBillingAddressResult = + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | Workspace + +input MutationWorkspaceUpdateBillingEmailInput { + email: String! + id: ID! +} + +union MutationWorkspaceUpdateBillingEmailResult = + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | Workspace + +input MutationWorkspaceUpdateInput { + displayName: String + id: ID! +} + +union MutationWorkspaceUpdateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace + +interface Node { + id: String! +} + +enum NumberPredicateFnType { + NumberPredicateFnEQ + NumberPredicateFnGT + NumberPredicateFnGTE + NumberPredicateFnLT + NumberPredicateFnLTE +} + +interface Offer { + context: OfferContext! + id: ID! + price: Price +} + +type OfferAbstract implements Offer { + context: OfferContext! + feature: FeatureAbstract! + id: ID! + price: Price +} + +union OfferContext = Plan | PlanSubscription + +type OfferResourceAggregation implements Offer { + context: OfferContext! + feature: FeatureResourceAggregation! + id: ID! + limit: Limit + price: Price + timeInterval: OfferTimeInterval +} + +type OfferResourceProperty implements Offer { + context: OfferContext! + feature: FeatureResourceProperty! + id: ID! + limit: Limit + price: Price + timeInterval: OfferTimeInterval +} + +union OfferTimeInterval = OfferTimeIntervalCycle | OfferTimeIntervalPrevious + +type OfferTimeIntervalCycle { + ok: Boolean! +} + +type OfferTimeIntervalPrevious { + milliseconds: Int! +} + +type OfferValue implements Offer { + context: OfferContext! + feature: FeatureValue! + id: ID! + limit: Limit + price: Price + value: String! +} + +enum Order { + asc + desc +} + +type PaymentMethod { + card: PaymentMethodCard! + id: ID! + isDefault: Boolean! +} + +type PaymentMethodCard { + brand: PaymentMethodCardBrand! + expiryMonth: Int! + expiryYear: Int! + id: ID! + last4: String! +} + +enum PaymentMethodCardBrand { + amex + diners + discover + eftpos_au + jcb + mastercard + unionpay + unknown + visa +} + +type Percentile { + percentile: Int! + value: MetricValue! +} + +type PhysicalAddress { + addressLine1: String + addressLine2: String + city: String + country: String + postalCodeOrZIP: String + region: String +} + +input PhysicalAddressInput { + addressLine1: String + addressLine2: String + city: String + country: CountryCode + postalCodeOrZIP: String + region: String +} + +type Plan { + displayName: String! + handle: String! + id: ID! + isDefault: Boolean! + isFree: Boolean! + offers: PlanOffers! + power: Int! + selectable: Boolean! + version: Int! + versionIsLatest: Boolean! + versions: PlanVersions! +} + +type PlanOffers { + accelerate: PlanOffersAccelerate! + conductor: PlanOffersConductor! + platform: PlanOffersPlatform! +} + +type PlanOffersAccelerate { + egress: OfferResourceProperty! + purgeCache: OfferResourceAggregation! + query: OfferResourceAggregation! +} + +type PlanOffersConductor { + createProject: OfferResourceAggregation! + organizationRole: OfferResourceProperty! +} + +type PlanOffersPlatform { + access: OfferAbstract! + support: OfferValue! +} + +type PlanSubscription { + createdAt: Date! + id: ID! + plan: Plan! + stripeSubscriptionId: String + stripeSubscriptionLineItems: [StripeSubscriptionLineItem!]! + workspace: Workspace! +} + +type PlanVersions { + isLatest: Boolean! + next: [Plan!]! + previous: [Plan!]! +} + +enum PreviousDateHandle { + last6h + last7d + last24h + last30d + last30m + startOfCycle +} + +union Price = PriceConstant | PriceTiered + +type PriceConstant implements PriceI { + cents: Int! + id: String! + stripePriceId: ID! +} + +interface PriceI { + id: String! + stripePriceId: ID! +} + +type PriceTiered implements PriceI { + id: String! + stripePriceId: ID! + tiers: [PriceTieredTier!]! +} + +type PriceTieredTier { + cents: Float! + from: Int! + to: Int +} + +type ProductHold { + createdAt: Int! + expiresAt: Int! + reason: String! +} + +interface ProductStatus { + enabled: Boolean! +} + +type Project { + accelerate: EnvironmentAccelerate! + createdAt: Date! + displayName: String! + environments: [Environment!]! + id: ID! + pulse: EnvironmentPulse! + workspace: Workspace! +} + +type ProjectNode implements Node { + createdAt: Date! + displayName: String! + id: String! + workspaceId: ID! +} + +union PulseStatus = PulseStatusDisabled | PulseStatusEnabled + +type PulseStatusDisabled implements ProductStatus { + enabled: Boolean! +} + +type PulseStatusEnabled implements ProductStatus { + enabled: Boolean! + error: String +} + +type Query { + environment(id: ID!): QueryEnvironmentResult! + me: Me! + plan(handle: String, id: ID, version: Int): QueryPlanResult! + project(id: ID!): QueryProjectResult! + serviceKeys(projectId: ID!): [ServiceKey!]! + system: System! + user(auth0Id: ID, id: ID): User! + workspace(id: ID!): QueryWorkspaceResult! +} + +union QueryEnvironmentResult = + | Environment + | ErrorInternal + | ErrorUserBusinessNotAuthorized + | ErrorUserBusinessResourceNotFound + +union QueryPlanResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ErrorUserInput | Plan + +union QueryProjectResult = + | ErrorInternal + | ErrorUserBusinessNotAuthorized + | ErrorUserBusinessResourceNotFound + | Project + +union QueryWorkspaceResult = + | ErrorInternal + | ErrorUserBusinessNotAuthorized + | ErrorUserBusinessResourceNotFound + | Workspace + +enum ResourceType { + Project + Workspace +} + +type ServiceKey { + createdAt: Date! + displayName: String! + id: ID! + valueHint: String! +} + +type ServiceKeyNode implements Node { + displayName: String! + id: String! + valueHint: String! +} + +type ServiceKeyWithValue { + serviceKey: ServiceKey! + value: ID! +} + +type SideEffectConfirmation { + ok: Boolean! +} + +enum StorageUnit { + bytes +} + +type StorageValue { + number: Float! + unit: StorageUnit +} + +type StripeSubscriptionLineItem { + feature: FeatureHandle! + id: ID! +} + +type System { + accelerate: SystemAccelerate! + plans: [Plan!]! + pulse: SystemPulse! +} + +type SystemAccelerate { + defaultRegion: SystemAccelerateRegion! + regions: [SystemAccelerateRegion!]! +} + +type SystemAccelerateRegion { + displayName: String! + id: ID! +} + +type SystemPulse { + defaultRegion: SystemAccelerateRegion! + regions: [SystemAccelerateRegion!]! +} + +type TimeInterval { + from: Date! + to: Date! +} + +input TimeIntervalInput { + fromDate: Date + fromDateHandle: PreviousDateHandle + toDate: Date +} + +type UsageProductAccelerate { + egress: UsageProductAccelerateFeatureEgress! + request: UsageProductAccelerateFeatureRequest! +} + +type UsageProductAccelerateFeatureEgress { + averageResponseSize: Float! + total: Float! +} + +type UsageProductAccelerateFeatureRequest { + all: UsageProductAccelerateFeatureRequestFilterAll! + cacheHit: UsageProductAccelerateFeatureRequestFilterCacheHit! +} + +type UsageProductAccelerateFeatureRequestFilterAll { + count: Int! +} + +type UsageProductAccelerateFeatureRequestFilterCacheHit { + ratioToMiss: Int! +} + +type User { + displayName: String + email: String! + featureFlags: UserFeatureFlags! + handle: String + id: ID! + image: String + preferences: UserPreferences! +} + +type UserFeatureFlags { + adminDashboard: Boolean! + mars: Boolean! + mercury: Boolean! + venus: Boolean! +} + +type UserPreferences { + """ + If null, user's default organization could have been deleted by another user. + """ + defaultWorkspace: Workspace +} + +type Workspace { + billingAddress: PhysicalAddress + billingEmail: String! + createdAt: Date! + displayName: String! + id: ID! + + """ + Whether this is the last workspace of the user or not. + """ + isUsersLastMembership: Boolean! + memberships: [WorkspaceMembership!]! + paymentMethods: [PaymentMethod!]! + planSubscription: PlanSubscription! + projects: [Project!]! + stripeCustomerId: String! + usage(timeInterval: TimeIntervalInput): WorkspaceUsage! +} + +type WorkspaceMembership { + id: ID! + role: WorkspaceRole! + user: User! +} + +type WorkspaceMembershipNode implements Node { + id: String! + workspaceId: ID! +} + +type WorkspaceNode implements Node { + billingEmail: String! + displayName: String! + id: String! +} + +input WorkspaceOrderBy { + displayName: Order = asc +} + +enum WorkspaceRole { + accountant + admin + developer + viewer +} + +type WorkspaceUsage { + accelerate: UsageProductAccelerate! + timeInterval: TimeInterval! +} diff --git a/src/demo.ts b/src/demo.ts new file mode 100644 index 000000000..61e901c6f --- /dev/null +++ b/src/demo.ts @@ -0,0 +1,1233 @@ +namespace $ { + export interface Scalars { + Boolean: boolean + Int: number + String: string + ID: string + Date: any + Float: number + } +} + +// ------------------------------------------------------------ // +// RootTypes // +// ------------------------------------------------------------ // + +interface Mutation { + accelerateCachePurge: ErrorInternal | SideEffectConfirmation + accelerateDisable: ErrorInternal | SideEffectConfirmation + accelerateEnable: ErrorInternal | SideEffectConfirmation + databaseLinkCreate: DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound + databaseLinkDelete: DatabaseLinkNode | ErrorInternal | ErrorUserBusinessResourceNotFound + databaseLinkUpdate: DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound + environmentCreate: + | Environment + | ErrorInternal + | ErrorUserBusinessPlanLimitHit + | ErrorUserBusinessResourceNotFound + environmentDelete: Environment | ErrorInternal | ErrorUserBusinessResourceNotFound + environmentUpdate: Environment | ErrorInternal | ErrorUserBusinessResourceNotFound + projectCreate: ErrorInternal | ErrorUserBusinessPlanLimitHit | ErrorUserBusinessResourceNotFound | Project + projectDelete: ErrorInternal | ErrorUserBusinessResourceNotFound | ProjectNode + projectUpdate: ErrorInternal | ErrorUserBusinessResourceNotFound | Project + pulseDisable: ErrorInternal | ErrorUserBusinessResourceNotFound | SideEffectConfirmation + pulseEnable: ErrorInternal | ErrorUser | SideEffectConfirmation + serviceKeyCreate: ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyWithValue + serviceKeyDelete: ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyNode + userUpdate: ErrorInternal | ErrorUserBusinessResourceNotFound | User + userUpdateDefaultWorkspace: ErrorInternal | ErrorUserBusinessResourceNotFound | User + workspaceCreate: ErrorInternal | Workspace + workspaceDelete: + | ErrorInternal + | ErrorUserBusinessDeleteWorkspaceOnPaidPlan + | ErrorUserBusinessResourceNotFound + | WorkspaceNode + workspaceMembershipCreate: + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | ErrorUserBusinessUserAlreadyMemberOfOrganization + | WorkspaceMembership + workspaceMembershipDelete: ErrorInternal | ErrorUserBusinessResourceNotFound | WorkspaceMembershipNode + workspacePlanSubscriptionChange: ErrorInternal | ErrorUserBusinessResourceNotFound | PlanSubscription + workspaceUpdate: ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace + workspaceUpdateBillingAddress: ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace + workspaceUpdateBillingEmail: ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace +} + +interface Query { + environment: + | Environment + | ErrorInternal + | ErrorUserBusinessNotAuthorized + | ErrorUserBusinessResourceNotFound + me: Me + plan: ErrorInternal | ErrorUserBusinessResourceNotFound | ErrorUserInput | Plan + project: ErrorInternal | ErrorUserBusinessNotAuthorized | ErrorUserBusinessResourceNotFound | Project + serviceKeys: Array + system: System + user: User + workspace: ErrorInternal | ErrorUserBusinessNotAuthorized | ErrorUserBusinessResourceNotFound | Workspace +} + +// ------------------------------------------------------------ // +// Enum // +// ------------------------------------------------------------ // + +type CountryCode = + | 'AD' + | 'AE' + | 'AF' + | 'AG' + | 'AI' + | 'AL' + | 'AM' + | 'AO' + | 'AQ' + | 'AR' + | 'AS' + | 'AT' + | 'AU' + | 'AW' + | 'AX' + | 'AZ' + | 'BA' + | 'BB' + | 'BD' + | 'BE' + | 'BF' + | 'BG' + | 'BH' + | 'BI' + | 'BJ' + | 'BL' + | 'BM' + | 'BN' + | 'BO' + | 'BQ' + | 'BR' + | 'BS' + | 'BT' + | 'BV' + | 'BW' + | 'BY' + | 'BZ' + | 'CA' + | 'CC' + | 'CD' + | 'CF' + | 'CG' + | 'CH' + | 'CI' + | 'CK' + | 'CL' + | 'CM' + | 'CN' + | 'CO' + | 'CR' + | 'CU' + | 'CV' + | 'CW' + | 'CX' + | 'CY' + | 'CZ' + | 'DE' + | 'DJ' + | 'DK' + | 'DM' + | 'DO' + | 'DZ' + | 'EC' + | 'EE' + | 'EG' + | 'EH' + | 'ER' + | 'ES' + | 'ET' + | 'FI' + | 'FJ' + | 'FK' + | 'FM' + | 'FO' + | 'FR' + | 'GA' + | 'GB' + | 'GD' + | 'GE' + | 'GF' + | 'GG' + | 'GH' + | 'GI' + | 'GL' + | 'GM' + | 'GN' + | 'GP' + | 'GQ' + | 'GR' + | 'GS' + | 'GT' + | 'GU' + | 'GW' + | 'GY' + | 'HK' + | 'HM' + | 'HN' + | 'HR' + | 'HT' + | 'HU' + | 'ID' + | 'IE' + | 'IL' + | 'IM' + | 'IN' + | 'IO' + | 'IQ' + | 'IR' + | 'IS' + | 'IT' + | 'JE' + | 'JM' + | 'JO' + | 'JP' + | 'KE' + | 'KG' + | 'KH' + | 'KI' + | 'KM' + | 'KN' + | 'KP' + | 'KR' + | 'KW' + | 'KY' + | 'KZ' + | 'LA' + | 'LB' + | 'LC' + | 'LI' + | 'LK' + | 'LR' + | 'LS' + | 'LT' + | 'LU' + | 'LV' + | 'LY' + | 'MA' + | 'MC' + | 'MD' + | 'ME' + | 'MF' + | 'MG' + | 'MH' + | 'MK' + | 'ML' + | 'MM' + | 'MN' + | 'MO' + | 'MP' + | 'MQ' + | 'MR' + | 'MS' + | 'MT' + | 'MU' + | 'MV' + | 'MW' + | 'MX' + | 'MY' + | 'MZ' + | 'NA' + | 'NC' + | 'NE' + | 'NF' + | 'NG' + | 'NI' + | 'NL' + | 'NO' + | 'NP' + | 'NR' + | 'NU' + | 'NZ' + | 'OM' + | 'PA' + | 'PE' + | 'PF' + | 'PG' + | 'PH' + | 'PK' + | 'PL' + | 'PM' + | 'PN' + | 'PR' + | 'PS' + | 'PT' + | 'PW' + | 'PY' + | 'QA' + | 'RE' + | 'RO' + | 'RS' + | 'RU' + | 'RW' + | 'SA' + | 'SB' + | 'SC' + | 'SD' + | 'SE' + | 'SG' + | 'SH' + | 'SI' + | 'SJ' + | 'SK' + | 'SL' + | 'SM' + | 'SN' + | 'SO' + | 'SR' + | 'SS' + | 'ST' + | 'SV' + | 'SX' + | 'SY' + | 'SZ' + | 'TC' + | 'TD' + | 'TF' + | 'TG' + | 'TH' + | 'TJ' + | 'TK' + | 'TL' + | 'TM' + | 'TN' + | 'TO' + | 'TR' + | 'TT' + | 'TV' + | 'TW' + | 'TZ' + | 'UA' + | 'UG' + | 'UM' + | 'US' + | 'UY' + | 'UZ' + | 'VA' + | 'VC' + | 'VE' + | 'VG' + | 'VI' + | 'VN' + | 'VU' + | 'WF' + | 'WS' + | 'YE' + | 'YT' + | 'ZA' + | 'ZM' + | 'ZW' + +type EnvironmentAccelerateUsageTimeWindowInput = 'last6h' | 'last7d' | 'last24h' | 'last30d' | 'last30m' + +type FeatureHandle = + | 'accelerateEgress' + | 'acceleratePurgeCache' + | 'accelerateQuery' + | 'access' + | 'createProject' + | 'organizationRole' + | 'platformSupport' + +type FeatureResourceAggregationValueResolverType = 'count' + +type MetricUnit = 'ms' | 'percent' + +type NumberPredicateFnType = + | 'NumberPredicateFnEQ' + | 'NumberPredicateFnGT' + | 'NumberPredicateFnGTE' + | 'NumberPredicateFnLT' + | 'NumberPredicateFnLTE' + +type Order = 'asc' | 'desc' + +type PaymentMethodCardBrand = + | 'amex' + | 'diners' + | 'discover' + | 'eftpos_au' + | 'jcb' + | 'mastercard' + | 'unionpay' + | 'unknown' + | 'visa' + +type PreviousDateHandle = 'last6h' | 'last7d' | 'last24h' | 'last30d' | 'last30m' | 'startOfCycle' + +type ResourceType = 'Project' | 'Workspace' + +type StorageUnit = 'bytes' + +type WorkspaceRole = 'accountant' | 'admin' | 'developer' | 'viewer' + +// ------------------------------------------------------------ // +// InputObject // +// ------------------------------------------------------------ // + +interface MutationAccelerateCachePurgeInput { + environmentId: $.Scalars['ID'] +} + +interface MutationAccelerateDisableInput { + environmentId: $.Scalars['ID'] +} + +interface MutationAccelerateEnableInput { + databaseLinkId: $.Scalars['ID'] +} + +interface MutationDatabaseLinkCreateInput { + connectionString: $.Scalars['String'] + displayName: $.Scalars['String'] | null + environmentId: $.Scalars['ID'] + regionId: $.Scalars['String'] | null +} + +interface MutationDatabaseLinkDeleteInput { + id: $.Scalars['ID'] +} + +interface MutationDatabaseLinkUpdateInput { + connectionString: $.Scalars['String'] + id: $.Scalars['ID'] + regionId: $.Scalars['String'] +} + +interface MutationEnvironmentCreateInput { + displayName: $.Scalars['String'] | null + isDefault: $.Scalars['Boolean'] | null + projectId: $.Scalars['ID'] +} + +interface MutationEnvironmentDeleteInput { + id: $.Scalars['ID'] +} + +interface MutationEnvironmentUpdateInput { + displayName: $.Scalars['String'] | null + id: $.Scalars['ID'] + isDefault: $.Scalars['Boolean'] | null +} + +interface MutationProjectCreateInput { + displayName: $.Scalars['String'] | null + workspaceId: $.Scalars['ID'] +} + +interface MutationProjectDeleteInput { + id: $.Scalars['ID'] +} + +interface MutationProjectUpdateInput { + displayName: $.Scalars['String'] | null + id: $.Scalars['ID'] +} + +interface MutationPulseDisableInput { + environmentId: $.Scalars['String'] +} + +interface MutationPulseEnableInput { + databaseLinkId: $.Scalars['String'] +} + +interface MutationServiceKeyCreateInput { + displayName: $.Scalars['String'] | null + environmentId: $.Scalars['ID'] +} + +interface MutationServiceKeyDeleteInput { + id: $.Scalars['String'] +} + +interface MutationUserUpdateDefaultWorkspaceInput { + workspaceId: $.Scalars['ID'] +} + +interface MutationUserUpdateInput { + displayName: $.Scalars['String'] | null + id: $.Scalars['ID'] +} + +interface MutationWorkspaceCreateInput { + displayName: $.Scalars['String'] | null +} + +interface MutationWorkspaceDeleteInput { + id: $.Scalars['ID'] +} + +interface MutationWorkspaceMembershipCreateInput { + email: $.Scalars['String'] + role: WorkspaceRole + workspaceId: $.Scalars['ID'] +} + +interface MutationWorkspaceMembershipDeleteInput { + id: $.Scalars['ID'] +} + +interface MutationWorkspacePlanSubscriptionChangeInput { + targetPlanId: $.Scalars['ID'] + workspaceId: $.Scalars['ID'] +} + +interface MutationWorkspaceUpdateBillingAddressInput { + address: PhysicalAddressInput + id: $.Scalars['ID'] +} + +interface MutationWorkspaceUpdateBillingEmailInput { + email: $.Scalars['String'] + id: $.Scalars['ID'] +} + +interface MutationWorkspaceUpdateInput { + displayName: $.Scalars['String'] | null + id: $.Scalars['ID'] +} + +interface PhysicalAddressInput { + addressLine1: $.Scalars['String'] | null + addressLine2: $.Scalars['String'] | null + city: $.Scalars['String'] | null + country: CountryCode | null + postalCodeOrZIP: $.Scalars['String'] | null + region: $.Scalars['String'] | null +} + +interface TimeIntervalInput { + fromDate: $.Scalars['Date'] | null + fromDateHandle: PreviousDateHandle | null + toDate: $.Scalars['Date'] | null +} + +interface WorkspaceOrderBy { + displayName: Order | null +} + +// ------------------------------------------------------------ // +// Interface // +// ------------------------------------------------------------ // + +interface Error { + message: $.Scalars['String'] +} + +interface Feature { + displayName: $.Scalars['String'] | null + handle: FeatureHandle + id: $.Scalars['ID'] + stripeProductId: $.Scalars['String'] +} + +interface Node { + id: $.Scalars['String'] +} + +interface Offer { + context: Plan | PlanSubscription + id: $.Scalars['ID'] + price: PriceConstant | PriceTiered | null +} + +interface PriceI { + id: $.Scalars['String'] + stripePriceId: $.Scalars['ID'] +} + +interface ProductStatus { + enabled: $.Scalars['Boolean'] +} + +// ------------------------------------------------------------ // +// Object // +// ------------------------------------------------------------ // + +interface AccelerateStatusDisabled { + enabled: $.Scalars['Boolean'] +} + +interface AccelerateStatusEnabled { + enabled: $.Scalars['Boolean'] +} + +interface Count { + number: $.Scalars['Int'] +} + +interface DatabaseLink { + connectionStringHint: $.Scalars['String'] + id: $.Scalars['ID'] + protocol: $.Scalars['String'] + region: $.Scalars['String'] | null +} + +interface DatabaseLinkNode { + connectionStringHint: $.Scalars['ID'] + displayName: $.Scalars['String'] + id: $.Scalars['String'] +} + +interface Environment { + accelerate: EnvironmentAccelerate + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + id: $.Scalars['ID'] + isDefault: $.Scalars['Boolean'] + project: Project + pulse: EnvironmentPulse + serviceKeys: Array + tenantId: $.Scalars['ID'] +} + +interface EnvironmentAccelerate { + databaseLink: DatabaseLink | null + holds: Array + status: AccelerateStatusDisabled | AccelerateStatusEnabled + usage: EnvironmentAccelerateUsage +} + +interface EnvironmentAccelerateTimeSeriesPoints { + queries: EnvironmentAccelerateUsageTimeSeriesPointsQueries + timestamps: Array<$.Scalars['Date']> +} + +interface EnvironmentAccelerateUsage { + latency: EnvironmentAccelerateUsageLatency + overview: EnvironmentAccelerateUsageOverview + timeInterval: TimeInterval + timeSeries: EnvironmentAccelerateUsageTimeSeries +} + +interface EnvironmentAccelerateUsageLatency { + queries: EnvironmentAccelerateUsageLatencyQueries +} + +interface EnvironmentAccelerateUsageLatencyQueries { + cached: EnvironmentAccelerateUsageLatencyQuery + origin: EnvironmentAccelerateUsageLatencyQuery +} + +interface EnvironmentAccelerateUsageLatencyQuery { + count: Count + durationAverage: MetricValue + durationPercentiles: Array +} + +interface EnvironmentAccelerateUsageOverview { + egress: EnvironmentAccelerateUsageOverviewEgress + queries: EnvironmentAccelerateUsageOverviewQueries +} + +interface EnvironmentAccelerateUsageOverviewCacheHit { + ratioToMiss: MetricValue +} + +interface EnvironmentAccelerateUsageOverviewEgress { + averageResponseSize: StorageValue + requestsServedFromOrigin: Count + total: StorageValue +} + +interface EnvironmentAccelerateUsageOverviewQueries { + cacheHit: EnvironmentAccelerateUsageOverviewCacheHit + cacheableCount: Count + totalCount: Count +} + +interface EnvironmentAccelerateUsageTimeSeries { + points: EnvironmentAccelerateTimeSeriesPoints +} + +interface EnvironmentAccelerateUsageTimeSeriesPointsQueries { + miss: Array | null + none: Array | null + swr: Array | null + ttl: Array | null +} + +interface EnvironmentAccelerateUsageTimeSeriesPointsQuery { + count: Count + timestamp: $.Scalars['Date'] +} + +interface EnvironmentPulse { + databaseLink: DatabaseLink | null + status: PulseStatusDisabled | PulseStatusEnabled +} + +interface ErrorInternal { + message: $.Scalars['String'] +} + +interface ErrorUser { + message: $.Scalars['String'] +} + +interface ErrorUserBusinessDeleteWorkspaceOnPaidPlan { + context: ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext + message: $.Scalars['String'] +} + +interface ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext { + plan: Plan +} + +interface ErrorUserBusinessNotAuthorized { + message: $.Scalars['String'] +} + +interface ErrorUserBusinessPlanLimitHit { + context: ErrorUserBusinessPlanLimitHitContext + message: $.Scalars['String'] +} + +interface ErrorUserBusinessPlanLimitHitContext { + featureHandle: $.Scalars['String'] | null +} + +interface ErrorUserBusinessResourceNotFound { + context: ErrorUserBusinessResourceNotFoundContext + message: $.Scalars['String'] +} + +interface ErrorUserBusinessResourceNotFoundContext { + id: $.Scalars['ID'] | null + typeName: ResourceType +} + +interface ErrorUserBusinessUserAlreadyMemberOfOrganization { + context: ErrorUserBusinessUserAlreadyMemberOfOrganizationContext + message: $.Scalars['String'] +} + +interface ErrorUserBusinessUserAlreadyMemberOfOrganizationContext { + user: User + workspace: Workspace +} + +interface ErrorUserInput { + message: $.Scalars['String'] +} + +interface FeatureAbstract { + displayName: $.Scalars['String'] | null + handle: FeatureHandle + id: $.Scalars['ID'] + stripeProductId: $.Scalars['String'] +} + +interface FeatureResourceAggregation { + displayName: $.Scalars['String'] | null + handle: FeatureHandle + id: $.Scalars['ID'] + resource: $.Scalars['String'] + scope: $.Scalars['String'] + stripeProductId: $.Scalars['String'] + valueResolver: FeatureResourceAggregationValueResolver +} + +interface FeatureResourceAggregationValueResolver { + type: FeatureResourceAggregationValueResolverType +} + +interface FeatureResourceProperty { + displayName: $.Scalars['String'] | null + handle: FeatureHandle + id: $.Scalars['ID'] + resource: $.Scalars['String'] + scope: $.Scalars['String'] + stripeProductId: $.Scalars['String'] + valueResolver: FeatureResourcePropertyValueResolver +} + +interface FeatureResourcePropertyValueResolver { + field: $.Scalars['String'] + type: FeatureValueTypeBoolean | FeatureValueTypeEnum | FeatureValueTypeNumber | FeatureValueTypeString +} + +interface FeatureValue { + displayName: $.Scalars['String'] | null + handle: FeatureHandle + id: $.Scalars['ID'] + stripeProductId: $.Scalars['String'] + valueType: FeatureValueTypeBoolean | FeatureValueTypeEnum | FeatureValueTypeNumber | FeatureValueTypeString +} + +interface FeatureValueTypeBoolean { + displayName: $.Scalars['String'] +} + +interface FeatureValueTypeEnum { + displayName: $.Scalars['String'] + members: Array +} + +interface FeatureValueTypeEnumMember { + description: $.Scalars['String'] | null + value: $.Scalars['String'] +} + +interface FeatureValueTypeNumber { + displayName: $.Scalars['String'] +} + +interface FeatureValueTypeString { + displayName: $.Scalars['String'] +} + +interface LimitEnum { + allowed: Array<$.Scalars['String']> +} + +interface LimitNumber { + amount: $.Scalars['Int'] + type: NumberPredicateFnType +} + +interface Me { + user: User + workspaces: Array +} + +interface MetricValue { + number: $.Scalars['Float'] + unit: MetricUnit | null +} + +interface OfferAbstract { + context: Plan | PlanSubscription + feature: FeatureAbstract + id: $.Scalars['ID'] + price: PriceConstant | PriceTiered | null +} + +interface OfferResourceAggregation { + context: Plan | PlanSubscription + feature: FeatureResourceAggregation + id: $.Scalars['ID'] + limit: LimitEnum | LimitNumber | null + price: PriceConstant | PriceTiered | null + timeInterval: OfferTimeIntervalCycle | OfferTimeIntervalPrevious | null +} + +interface OfferResourceProperty { + context: Plan | PlanSubscription + feature: FeatureResourceProperty + id: $.Scalars['ID'] + limit: LimitEnum | LimitNumber | null + price: PriceConstant | PriceTiered | null + timeInterval: OfferTimeIntervalCycle | OfferTimeIntervalPrevious | null +} + +interface OfferTimeIntervalCycle { + ok: $.Scalars['Boolean'] +} + +interface OfferTimeIntervalPrevious { + milliseconds: $.Scalars['Int'] +} + +interface OfferValue { + context: Plan | PlanSubscription + feature: FeatureValue + id: $.Scalars['ID'] + limit: LimitEnum | LimitNumber | null + price: PriceConstant | PriceTiered | null + value: $.Scalars['String'] +} + +interface PaymentMethod { + card: PaymentMethodCard + id: $.Scalars['ID'] + isDefault: $.Scalars['Boolean'] +} + +interface PaymentMethodCard { + brand: PaymentMethodCardBrand + expiryMonth: $.Scalars['Int'] + expiryYear: $.Scalars['Int'] + id: $.Scalars['ID'] + last4: $.Scalars['String'] +} + +interface Percentile { + percentile: $.Scalars['Int'] + value: MetricValue +} + +interface PhysicalAddress { + addressLine1: $.Scalars['String'] | null + addressLine2: $.Scalars['String'] | null + city: $.Scalars['String'] | null + country: $.Scalars['String'] | null + postalCodeOrZIP: $.Scalars['String'] | null + region: $.Scalars['String'] | null +} + +interface Plan { + displayName: $.Scalars['String'] + handle: $.Scalars['String'] + id: $.Scalars['ID'] + isDefault: $.Scalars['Boolean'] + isFree: $.Scalars['Boolean'] + offers: PlanOffers + power: $.Scalars['Int'] + selectable: $.Scalars['Boolean'] + version: $.Scalars['Int'] + versionIsLatest: $.Scalars['Boolean'] + versions: PlanVersions +} + +interface PlanOffers { + accelerate: PlanOffersAccelerate + conductor: PlanOffersConductor + platform: PlanOffersPlatform +} + +interface PlanOffersAccelerate { + egress: OfferResourceProperty + purgeCache: OfferResourceAggregation + query: OfferResourceAggregation +} + +interface PlanOffersConductor { + createProject: OfferResourceAggregation + organizationRole: OfferResourceProperty +} + +interface PlanOffersPlatform { + access: OfferAbstract + support: OfferValue +} + +interface PlanSubscription { + createdAt: $.Scalars['Date'] + id: $.Scalars['ID'] + plan: Plan + stripeSubscriptionId: $.Scalars['String'] | null + stripeSubscriptionLineItems: Array + workspace: Workspace +} + +interface PlanVersions { + isLatest: $.Scalars['Boolean'] + next: Array + previous: Array +} + +interface PriceConstant { + cents: $.Scalars['Int'] + id: $.Scalars['String'] + stripePriceId: $.Scalars['ID'] +} + +interface PriceTiered { + id: $.Scalars['String'] + stripePriceId: $.Scalars['ID'] + tiers: Array +} + +interface PriceTieredTier { + cents: $.Scalars['Float'] + from: $.Scalars['Int'] + to: $.Scalars['Int'] | null +} + +interface ProductHold { + createdAt: $.Scalars['Int'] + expiresAt: $.Scalars['Int'] + reason: $.Scalars['String'] +} + +interface Project { + accelerate: EnvironmentAccelerate + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + environments: Array + id: $.Scalars['ID'] + pulse: EnvironmentPulse + workspace: Workspace +} + +interface ProjectNode { + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + id: $.Scalars['String'] + workspaceId: $.Scalars['ID'] +} + +interface PulseStatusDisabled { + enabled: $.Scalars['Boolean'] +} + +interface PulseStatusEnabled { + enabled: $.Scalars['Boolean'] + error: $.Scalars['String'] | null +} + +interface ServiceKey { + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + id: $.Scalars['ID'] + valueHint: $.Scalars['String'] +} + +interface ServiceKeyNode { + displayName: $.Scalars['String'] + id: $.Scalars['String'] + valueHint: $.Scalars['String'] +} + +interface ServiceKeyWithValue { + serviceKey: ServiceKey + value: $.Scalars['ID'] +} + +interface SideEffectConfirmation { + ok: $.Scalars['Boolean'] +} + +interface StorageValue { + number: $.Scalars['Float'] + unit: StorageUnit | null +} + +interface StripeSubscriptionLineItem { + feature: FeatureHandle + id: $.Scalars['ID'] +} + +interface System { + accelerate: SystemAccelerate + plans: Array + pulse: SystemPulse +} + +interface SystemAccelerate { + defaultRegion: SystemAccelerateRegion + regions: Array +} + +interface SystemAccelerateRegion { + displayName: $.Scalars['String'] + id: $.Scalars['ID'] +} + +interface SystemPulse { + defaultRegion: SystemAccelerateRegion + regions: Array +} + +interface TimeInterval { + from: $.Scalars['Date'] + to: $.Scalars['Date'] +} + +interface UsageProductAccelerate { + egress: UsageProductAccelerateFeatureEgress + request: UsageProductAccelerateFeatureRequest +} + +interface UsageProductAccelerateFeatureEgress { + averageResponseSize: $.Scalars['Float'] + total: $.Scalars['Float'] +} + +interface UsageProductAccelerateFeatureRequest { + all: UsageProductAccelerateFeatureRequestFilterAll + cacheHit: UsageProductAccelerateFeatureRequestFilterCacheHit +} + +interface UsageProductAccelerateFeatureRequestFilterAll { + count: $.Scalars['Int'] +} + +interface UsageProductAccelerateFeatureRequestFilterCacheHit { + ratioToMiss: $.Scalars['Int'] +} + +interface User { + displayName: $.Scalars['String'] | null + email: $.Scalars['String'] + featureFlags: UserFeatureFlags + handle: $.Scalars['String'] | null + id: $.Scalars['ID'] + image: $.Scalars['String'] | null + preferences: UserPreferences +} + +interface UserFeatureFlags { + adminDashboard: $.Scalars['Boolean'] + mars: $.Scalars['Boolean'] + mercury: $.Scalars['Boolean'] + venus: $.Scalars['Boolean'] +} + +interface UserPreferences { + defaultWorkspace: Workspace | null +} + +interface Workspace { + billingAddress: PhysicalAddress | null + billingEmail: $.Scalars['String'] + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + id: $.Scalars['ID'] + isUsersLastMembership: $.Scalars['Boolean'] + memberships: Array + paymentMethods: Array + planSubscription: PlanSubscription + projects: Array + stripeCustomerId: $.Scalars['String'] + usage: WorkspaceUsage +} + +interface WorkspaceMembership { + id: $.Scalars['ID'] + role: WorkspaceRole + user: User +} + +interface WorkspaceMembershipNode { + id: $.Scalars['String'] + workspaceId: $.Scalars['ID'] +} + +interface WorkspaceNode { + billingEmail: $.Scalars['String'] + displayName: $.Scalars['String'] + id: $.Scalars['String'] +} + +interface WorkspaceUsage { + accelerate: UsageProductAccelerate + timeInterval: TimeInterval +} + +// ------------------------------------------------------------ // +// Union // +// ------------------------------------------------------------ // + +type AccelerateStatus = AccelerateStatusDisabled | AccelerateStatusEnabled + +type FeatureValueType = + | FeatureValueTypeBoolean + | FeatureValueTypeEnum + | FeatureValueTypeNumber + | FeatureValueTypeString + +type Limit = LimitEnum | LimitNumber + +type MutationAccelerateCachePurgeResult = ErrorInternal | SideEffectConfirmation + +type MutationAccelerateDisableResult = ErrorInternal | SideEffectConfirmation + +type MutationAccelerateEnableResult = ErrorInternal | SideEffectConfirmation + +type MutationDatabaseLinkCreateResult = DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound + +type MutationDatabaseLinkDeleteResult = DatabaseLinkNode | ErrorInternal | ErrorUserBusinessResourceNotFound + +type MutationDatabaseLinkUpdateResult = DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound + +type MutationEnvironmentCreateResult = + | Environment + | ErrorInternal + | ErrorUserBusinessPlanLimitHit + | ErrorUserBusinessResourceNotFound + +type MutationEnvironmentDeleteResult = Environment | ErrorInternal | ErrorUserBusinessResourceNotFound + +type MutationEnvironmentUpdateResult = Environment | ErrorInternal | ErrorUserBusinessResourceNotFound + +type MutationProjectCreateResult = + | ErrorInternal + | ErrorUserBusinessPlanLimitHit + | ErrorUserBusinessResourceNotFound + | Project + +type MutationProjectDeleteResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ProjectNode + +type MutationProjectUpdateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | Project + +type MutationPulseDisableResult = ErrorInternal | ErrorUserBusinessResourceNotFound | SideEffectConfirmation + +type MutationPulseEnableResult = ErrorInternal | ErrorUser | SideEffectConfirmation + +type MutationServiceKeyCreateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyWithValue + +type MutationServiceKeyDeleteResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyNode + +type MutationUserUpdateDefaultWorkspaceResult = ErrorInternal | ErrorUserBusinessResourceNotFound | User + +type MutationUserUpdateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | User + +type MutationWorkspaceCreateResult = ErrorInternal | Workspace + +type MutationWorkspaceDeleteResult = + | ErrorInternal + | ErrorUserBusinessDeleteWorkspaceOnPaidPlan + | ErrorUserBusinessResourceNotFound + | WorkspaceNode + +type MutationWorkspaceMembershipCreateResult = + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | ErrorUserBusinessUserAlreadyMemberOfOrganization + | WorkspaceMembership + +type MutationWorkspaceMembershipDeleteResult = + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | WorkspaceMembershipNode + +type MutationWorkspacePlanSubscriptionChangeResult = + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | PlanSubscription + +type MutationWorkspaceUpdateBillingAddressResult = + | ErrorInternal + | ErrorUserBusinessResourceNotFound + | Workspace + +type MutationWorkspaceUpdateBillingEmailResult = ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace + +type MutationWorkspaceUpdateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace + +type OfferContext = Plan | PlanSubscription + +type OfferTimeInterval = OfferTimeIntervalCycle | OfferTimeIntervalPrevious + +type Price = PriceConstant | PriceTiered + +type PulseStatus = PulseStatusDisabled | PulseStatusEnabled + +type QueryEnvironmentResult = + | Environment + | ErrorInternal + | ErrorUserBusinessNotAuthorized + | ErrorUserBusinessResourceNotFound + +type QueryPlanResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ErrorUserInput | Plan + +type QueryProjectResult = + | ErrorInternal + | ErrorUserBusinessNotAuthorized + | ErrorUserBusinessResourceNotFound + | Project + +type QueryWorkspaceResult = + | ErrorInternal + | ErrorUserBusinessNotAuthorized + | ErrorUserBusinessResourceNotFound + | Workspace diff --git a/src/lib/generateTypes.ts b/src/lib/generateTypes.ts new file mode 100644 index 000000000..d8e50d861 --- /dev/null +++ b/src/lib/generateTypes.ts @@ -0,0 +1,249 @@ +import { readFileSync } from 'fs' +import type { GraphQLField, GraphQLInputField, GraphQLNamedType } from 'graphql' +import { + GraphQLEnumType, + GraphQLInputObjectType, + GraphQLInterfaceType, + GraphQLList, + GraphQLNonNull, + GraphQLObjectType, + GraphQLScalarType, + GraphQLUnionType, + OperationTypeNode, +} from 'graphql' +import { buildSchema } from 'graphql' + +type AnyGraphQLField = GraphQLField | GraphQLInputField +// type AnyGraphQLFieldMap = GraphQLFieldMap +// type AnyGraphQLNonNull = GraphQLNonNull +// type AnyGraphQLList = GraphQLList + +type AnyGraphQLFieldsType = GraphQLObjectType | GraphQLInterfaceType | GraphQLInputObjectType + +const NameToClassNamedType = { + GraphQLScalarType: GraphQLScalarType, + GraphQLObjectType: GraphQLObjectType, + GraphQLInterfaceType: GraphQLInterfaceType, + GraphQLUnionType: GraphQLUnionType, + GraphQLEnumType: GraphQLEnumType, + GraphQLInputObjectType: GraphQLInputObjectType, +} + +type NameToClassNamedType = typeof NameToClassNamedType + +const NameToClass = { + GraphQLNonNull: GraphQLNonNull, + GraphQLScalarType: GraphQLScalarType, + GraphQLObjectType: GraphQLObjectType, + GraphQLInterfaceType: GraphQLInterfaceType, + GraphQLUnionType: GraphQLUnionType, + GraphQLEnumType: GraphQLEnumType, + GraphQLInputObjectType: GraphQLInputObjectType, + GraphQLList: GraphQLList, +} as const + +type NameToClass = typeof NameToClass + +type AnyClass = InstanceType + +const definePointerRenderers = <$Renderers extends { [ClassName in keyof NameToClass]: any }>(renderers: { + [ClassName in keyof $Renderers]: ( + node: ClassName extends keyof NameToClass ? InstanceType : never, + ) => string +}) => renderers + +const defineConcreteRenderers = < + $Renderers extends { [ClassName in keyof NameToClassNamedType]: any }, +>(renderers: { + [ClassName in keyof $Renderers]: ( + node: ClassName extends keyof NameToClassNamedType + ? InstanceType + : never, + ) => string +}): { + [ClassName in keyof $Renderers]: ( + node: ClassName extends keyof NameToClass + ? InstanceType | null | undefined + : never, + ) => string +} => { + return Object.fromEntries( + Object.entries(renderers).map(([key, renderer]) => { + return [ + key, + (node: any) => { + if (!node) return `` + return renderer(node) //eslint-disable-line + }, + ] + }), + ) as any +} + +const dispatchToPointerRenderer = (node: AnyClass): string => { + // @ts-expect-error lookup + const renderer = pointerRenderers[node.constructor.name] //eslint-disable-line + if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) + return renderer(node) //eslint-disable-line +} + +const dispatchToConcreteRenderer = (node: GraphQLNamedType): string => { + // @ts-expect-error lookup + const renderer = concreteRenderers[node.constructor.name] //eslint-disable-line + if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) + return renderer(node) //eslint-disable-line +} + +const pointerRenderers = definePointerRenderers({ + GraphQLNonNull: (node) => dispatchToPointerRenderer(node.ofType), + GraphQLEnumType: (node) => node.name, + GraphQLInputObjectType: (node) => node.name, + GraphQLInterfaceType: (node) => node.name, + GraphQLList: (node) => Code.list(dispatchToPointerRenderer(node.ofType)), + GraphQLObjectType: (node) => node.name, + GraphQLScalarType: (node) => `$.Scalars[${Code.quote(node.name)}]`, + GraphQLUnionType: (node) => Code.unionItems(node.getTypes().map((_) => dispatchToPointerRenderer(_))), +}) + +const concreteRenderers = defineConcreteRenderers({ + GraphQLEnumType: (node) => + Code.union( + node.name, + node.getValues().map((_) => Code.quote(_.name)), + ), + GraphQLInputObjectType: (node) => Code.inter(node.name, renderFields(node)), + GraphQLInterfaceType: (node) => Code.inter(node.name, renderFields(node)), + GraphQLObjectType: (node) => Code.inter(node.name, renderFields(node)), + GraphQLScalarType: () => ``, + GraphQLUnionType: (node) => + Code.union( + node.name, + node.getTypes().map((_) => dispatchToPointerRenderer(_)), + ), +}) + +const renderFields = (node: AnyGraphQLFieldsType): string => { + return Code.fieldTypes( + Object.values(node.getFields()).map((field) => Code.fieldType(field.name, renderField(field))), + ) +} + +const renderField = (field: AnyGraphQLField): string => { + const [fieldType, nullable] = + field.type instanceof GraphQLNonNull ? [field.type.ofType, false] : [field.type, true] + return nullable ? Code.nullable(dispatchToPointerRenderer(fieldType)) : dispatchToPointerRenderer(fieldType) //eslint-disable-line +} + +namespace Code { + export const quote = (str: string) => `"${str}"` + export const nullable = (type: string) => `${type} | null` + export const union = (name: string, types: string[]) => `type ${name} =\n| ${Code.unionItems(types)}` + export const unionItems = (types: string[]) => types.join(`\n| `) + export const list = (type: string) => `Array<${type}>` + export const fieldType = (name: string, type: string) => `"${name}": ${type}` + export const fieldTypes = (fieldTypes: string[]) => fieldTypes.join(`\n`) + export const inter = (name: string, fields: string) => `interface ${name} {\n${fields}\n}` + export const commentSectionTitle = (title: string) => { + const lineSize = 60 + const line = `-`.repeat(lineSize) + const titlePrefixSpace = ` `.repeat(Math.round(lineSize / 2) - Math.round(title.length / 2)) + const titleSuffixSpace = ` `.repeat(lineSize - (titlePrefixSpace.length + title.length)) + return `\n\n// ${line} //\n// ${titlePrefixSpace + title + titleSuffixSpace} //\n// ${line} //\n\n` + } +} + +const scalarTypeMap: Record = { + ID: `string`, + Int: `number`, + String: `string`, + Float: `number`, + Boolean: `boolean`, +} + +// high level + +interface Input { + schemaSource: string +} + +const generateSchemaTypes = (input: Input) => { + const schema = buildSchema(input.schemaSource) + const typeMap = schema.getTypeMap() + const typeMapValues = Object.values(typeMap) + const typeMapByKind: { + [Name in keyof NameToClassNamedType]: InstanceType[] + } & { GraphQLRootTypes: GraphQLObjectType[] } = { + GraphQLRootTypes: [], + GraphQLScalarType: [], + GraphQLEnumType: [], + GraphQLInputObjectType: [], + GraphQLInterfaceType: [], + GraphQLObjectType: [], + GraphQLUnionType: [], + } + for (const type of typeMapValues) { + if (type.name.startsWith(`__`)) continue + switch (true) { + case type instanceof GraphQLScalarType: + typeMapByKind.GraphQLScalarType.push(type) + break + case type instanceof GraphQLEnumType: + typeMapByKind.GraphQLEnumType.push(type) + break + case type instanceof GraphQLInputObjectType: + typeMapByKind.GraphQLInputObjectType.push(type) + break + case type instanceof GraphQLInterfaceType: + typeMapByKind.GraphQLInterfaceType.push(type) + break + case type instanceof GraphQLObjectType: + if (type.name === `Query` || type.name === `Mutation` || type.name === `Subscription`) { + typeMapByKind.GraphQLRootTypes.push(type) + } else { + typeMapByKind.GraphQLObjectType.push(type) + } + break + case type instanceof GraphQLUnionType: + typeMapByKind.GraphQLUnionType.push(type) + break + default: + // skip + break + } + } + + let code = `` + + code += ` + namespace $ { + export interface Scalars { + ${typeMapByKind.GraphQLScalarType.map((_) => { + // todo strict mode where instead of falling back to "any" we throw an error + const type = scalarTypeMap[_.name] || `any` + return Code.fieldType(_.name, type) + }).join(`\n`)} + } + } + ` + + for (const [name, types] of Object.entries(typeMapByKind)) { + if (name === `GraphQLScalarType`) continue + + code += Code.commentSectionTitle(name.replace(/^GraphQL/, ``).replace(/Type$/, ``)) + if (types.length === 0) { + code += `// -- no types --\n` + continue + } + code += types.map(dispatchToConcreteRenderer).join(`\n\n`) + } + + return code +} + +// demo + +import fs from 'node:fs' + +const schemaSource = readFileSync(`./examples/schema.graphql`, `utf8`) +const code = generateSchemaTypes({ schemaSource }) +fs.writeFileSync(`./src/demo.ts`, code, { encoding: `utf8` }) From 3221e62d0616b6eb19684d7613c7981a70d40daa Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 17 Feb 2024 17:33:58 -0500 Subject: [PATCH 02/71] refactor --- src/lib/generateTypes.ts | 88 ++++------------------------------------ src/lib/graphql.ts | 84 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 79 deletions(-) create mode 100644 src/lib/graphql.ts diff --git a/src/lib/generateTypes.ts b/src/lib/generateTypes.ts index d8e50d861..df5ca6c02 100644 --- a/src/lib/generateTypes.ts +++ b/src/lib/generateTypes.ts @@ -1,17 +1,17 @@ +import type { AnyClass, NameToClassNamedType } from './graphql.js' +import { getTypeMapByKind, type NameToClass } from './graphql.js' import { readFileSync } from 'fs' -import type { GraphQLField, GraphQLInputField, GraphQLNamedType } from 'graphql' -import { - GraphQLEnumType, +import type { + GraphQLField, + GraphQLInputField, GraphQLInputObjectType, GraphQLInterfaceType, - GraphQLList, - GraphQLNonNull, + GraphQLNamedType, GraphQLObjectType, - GraphQLScalarType, - GraphQLUnionType, - OperationTypeNode, } from 'graphql' +import { GraphQLNonNull } from 'graphql' import { buildSchema } from 'graphql' +import fs from 'node:fs' type AnyGraphQLField = GraphQLField | GraphQLInputField // type AnyGraphQLFieldMap = GraphQLFieldMap @@ -20,32 +20,6 @@ type AnyGraphQLField = GraphQLField | GraphQLInputField type AnyGraphQLFieldsType = GraphQLObjectType | GraphQLInterfaceType | GraphQLInputObjectType -const NameToClassNamedType = { - GraphQLScalarType: GraphQLScalarType, - GraphQLObjectType: GraphQLObjectType, - GraphQLInterfaceType: GraphQLInterfaceType, - GraphQLUnionType: GraphQLUnionType, - GraphQLEnumType: GraphQLEnumType, - GraphQLInputObjectType: GraphQLInputObjectType, -} - -type NameToClassNamedType = typeof NameToClassNamedType - -const NameToClass = { - GraphQLNonNull: GraphQLNonNull, - GraphQLScalarType: GraphQLScalarType, - GraphQLObjectType: GraphQLObjectType, - GraphQLInterfaceType: GraphQLInterfaceType, - GraphQLUnionType: GraphQLUnionType, - GraphQLEnumType: GraphQLEnumType, - GraphQLInputObjectType: GraphQLInputObjectType, - GraphQLList: GraphQLList, -} as const - -type NameToClass = typeof NameToClass - -type AnyClass = InstanceType - const definePointerRenderers = <$Renderers extends { [ClassName in keyof NameToClass]: any }>(renderers: { [ClassName in keyof $Renderers]: ( node: ClassName extends keyof NameToClass ? InstanceType : never, @@ -168,49 +142,7 @@ interface Input { const generateSchemaTypes = (input: Input) => { const schema = buildSchema(input.schemaSource) - const typeMap = schema.getTypeMap() - const typeMapValues = Object.values(typeMap) - const typeMapByKind: { - [Name in keyof NameToClassNamedType]: InstanceType[] - } & { GraphQLRootTypes: GraphQLObjectType[] } = { - GraphQLRootTypes: [], - GraphQLScalarType: [], - GraphQLEnumType: [], - GraphQLInputObjectType: [], - GraphQLInterfaceType: [], - GraphQLObjectType: [], - GraphQLUnionType: [], - } - for (const type of typeMapValues) { - if (type.name.startsWith(`__`)) continue - switch (true) { - case type instanceof GraphQLScalarType: - typeMapByKind.GraphQLScalarType.push(type) - break - case type instanceof GraphQLEnumType: - typeMapByKind.GraphQLEnumType.push(type) - break - case type instanceof GraphQLInputObjectType: - typeMapByKind.GraphQLInputObjectType.push(type) - break - case type instanceof GraphQLInterfaceType: - typeMapByKind.GraphQLInterfaceType.push(type) - break - case type instanceof GraphQLObjectType: - if (type.name === `Query` || type.name === `Mutation` || type.name === `Subscription`) { - typeMapByKind.GraphQLRootTypes.push(type) - } else { - typeMapByKind.GraphQLObjectType.push(type) - } - break - case type instanceof GraphQLUnionType: - typeMapByKind.GraphQLUnionType.push(type) - break - default: - // skip - break - } - } + const typeMapByKind = getTypeMapByKind(schema) let code = `` @@ -242,8 +174,6 @@ const generateSchemaTypes = (input: Input) => { // demo -import fs from 'node:fs' - const schemaSource = readFileSync(`./examples/schema.graphql`, `utf8`) const code = generateSchemaTypes({ schemaSource }) fs.writeFileSync(`./src/demo.ts`, code, { encoding: `utf8` }) diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts new file mode 100644 index 000000000..7ec766750 --- /dev/null +++ b/src/lib/graphql.ts @@ -0,0 +1,84 @@ +import type { GraphQLSchema } from 'graphql' +import { + GraphQLEnumType, + GraphQLInputObjectType, + GraphQLInterfaceType, + GraphQLList, + GraphQLNonNull, + GraphQLObjectType, + GraphQLScalarType, + GraphQLUnionType, +} from 'graphql' + +export const getTypeMapByKind = (schema: GraphQLSchema) => { + const typeMap = schema.getTypeMap() + const typeMapValues = Object.values(typeMap) + const typeMapByKind: { + [Name in keyof NameToClassNamedType]: InstanceType[] + } & { GraphQLRootTypes: GraphQLObjectType[] } = { + GraphQLRootTypes: [], + GraphQLScalarType: [], + GraphQLEnumType: [], + GraphQLInputObjectType: [], + GraphQLInterfaceType: [], + GraphQLObjectType: [], + GraphQLUnionType: [], + } + for (const type of typeMapValues) { + if (type.name.startsWith(`__`)) continue + switch (true) { + case type instanceof GraphQLScalarType: + typeMapByKind.GraphQLScalarType.push(type) + break + case type instanceof GraphQLEnumType: + typeMapByKind.GraphQLEnumType.push(type) + break + case type instanceof GraphQLInputObjectType: + typeMapByKind.GraphQLInputObjectType.push(type) + break + case type instanceof GraphQLInterfaceType: + typeMapByKind.GraphQLInterfaceType.push(type) + break + case type instanceof GraphQLObjectType: + if (type.name === `Query` || type.name === `Mutation` || type.name === `Subscription`) { + typeMapByKind.GraphQLRootTypes.push(type) + } else { + typeMapByKind.GraphQLObjectType.push(type) + } + break + case type instanceof GraphQLUnionType: + typeMapByKind.GraphQLUnionType.push(type) + break + default: + // skip + break + } + } + return typeMapByKind +} + +export const NameToClassNamedType = { + GraphQLScalarType: GraphQLScalarType, + GraphQLObjectType: GraphQLObjectType, + GraphQLInterfaceType: GraphQLInterfaceType, + GraphQLUnionType: GraphQLUnionType, + GraphQLEnumType: GraphQLEnumType, + GraphQLInputObjectType: GraphQLInputObjectType, +} + +export type NameToClassNamedType = typeof NameToClassNamedType + +export const NameToClass = { + GraphQLNonNull: GraphQLNonNull, + GraphQLScalarType: GraphQLScalarType, + GraphQLObjectType: GraphQLObjectType, + GraphQLInterfaceType: GraphQLInterfaceType, + GraphQLUnionType: GraphQLUnionType, + GraphQLEnumType: GraphQLEnumType, + GraphQLInputObjectType: GraphQLInputObjectType, + GraphQLList: GraphQLList, +} as const + +export type NameToClass = typeof NameToClass + +export type AnyClass = InstanceType From 5e3448f9cd8007cc9fa3471e6dca24c8f6944324 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 17 Feb 2024 17:35:07 -0500 Subject: [PATCH 03/71] refactor --- src/lib/Code.ts | 17 +++++++++++++++++ src/lib/generateTypes.ts | 19 +------------------ 2 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 src/lib/Code.ts diff --git a/src/lib/Code.ts b/src/lib/Code.ts new file mode 100644 index 000000000..74aa6045f --- /dev/null +++ b/src/lib/Code.ts @@ -0,0 +1,17 @@ +export namespace Code { + export const quote = (str: string) => `"${str}"` + export const nullable = (type: string) => `${type} | null` + export const union = (name: string, types: string[]) => `type ${name} =\n| ${Code.unionItems(types)}` + export const unionItems = (types: string[]) => types.join(`\n| `) + export const list = (type: string) => `Array<${type}>` + export const fieldType = (name: string, type: string) => `"${name}": ${type}` + export const fieldTypes = (fieldTypes: string[]) => fieldTypes.join(`\n`) + export const inter = (name: string, fields: string) => `interface ${name} {\n${fields}\n}` + export const commentSectionTitle = (title: string) => { + const lineSize = 60 + const line = `-`.repeat(lineSize) + const titlePrefixSpace = ` `.repeat(Math.round(lineSize / 2) - Math.round(title.length / 2)) + const titleSuffixSpace = ` `.repeat(lineSize - (titlePrefixSpace.length + title.length)) + return `\n\n// ${line} //\n// ${titlePrefixSpace + title + titleSuffixSpace} //\n// ${line} //\n\n` + } +} diff --git a/src/lib/generateTypes.ts b/src/lib/generateTypes.ts index df5ca6c02..8929e672a 100644 --- a/src/lib/generateTypes.ts +++ b/src/lib/generateTypes.ts @@ -1,3 +1,4 @@ +import { Code } from './Code.js' import type { AnyClass, NameToClassNamedType } from './graphql.js' import { getTypeMapByKind, type NameToClass } from './graphql.js' import { readFileSync } from 'fs' @@ -108,24 +109,6 @@ const renderField = (field: AnyGraphQLField): string => { return nullable ? Code.nullable(dispatchToPointerRenderer(fieldType)) : dispatchToPointerRenderer(fieldType) //eslint-disable-line } -namespace Code { - export const quote = (str: string) => `"${str}"` - export const nullable = (type: string) => `${type} | null` - export const union = (name: string, types: string[]) => `type ${name} =\n| ${Code.unionItems(types)}` - export const unionItems = (types: string[]) => types.join(`\n| `) - export const list = (type: string) => `Array<${type}>` - export const fieldType = (name: string, type: string) => `"${name}": ${type}` - export const fieldTypes = (fieldTypes: string[]) => fieldTypes.join(`\n`) - export const inter = (name: string, fields: string) => `interface ${name} {\n${fields}\n}` - export const commentSectionTitle = (title: string) => { - const lineSize = 60 - const line = `-`.repeat(lineSize) - const titlePrefixSpace = ` `.repeat(Math.round(lineSize / 2) - Math.round(title.length / 2)) - const titleSuffixSpace = ` `.repeat(lineSize - (titlePrefixSpace.length + title.length)) - return `\n\n// ${line} //\n// ${titlePrefixSpace + title + titleSuffixSpace} //\n// ${line} //\n\n` - } -} - const scalarTypeMap: Record = { ID: `string`, Int: `number`, From 49f86d987872462a3761c95477f8264d81c41105 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 17 Feb 2024 18:03:16 -0500 Subject: [PATCH 04/71] namespaces --- src/demo.ts | 2366 +++++++++++++++++++++----------------- src/lib/Code.ts | 2 + src/lib/generateTypes.ts | 66 +- src/lib/graphql.ts | 12 +- src/lib/prelude.ts | 3 + 5 files changed, 1356 insertions(+), 1093 deletions(-) diff --git a/src/demo.ts b/src/demo.ts index 61e901c6f..721059af9 100644 --- a/src/demo.ts +++ b/src/demo.ts @@ -1,1233 +1,1465 @@ namespace $ { export interface Scalars { - Boolean: boolean - Int: number - String: string - ID: string - Date: any - Float: number + 'Boolean': boolean + 'Int': number + 'String': string + 'ID': string + 'Date': any + 'Float': number } } // ------------------------------------------------------------ // -// RootTypes // +// Root // // ------------------------------------------------------------ // -interface Mutation { - accelerateCachePurge: ErrorInternal | SideEffectConfirmation - accelerateDisable: ErrorInternal | SideEffectConfirmation - accelerateEnable: ErrorInternal | SideEffectConfirmation - databaseLinkCreate: DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound - databaseLinkDelete: DatabaseLinkNode | ErrorInternal | ErrorUserBusinessResourceNotFound - databaseLinkUpdate: DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound - environmentCreate: - | Environment - | ErrorInternal - | ErrorUserBusinessPlanLimitHit - | ErrorUserBusinessResourceNotFound - environmentDelete: Environment | ErrorInternal | ErrorUserBusinessResourceNotFound - environmentUpdate: Environment | ErrorInternal | ErrorUserBusinessResourceNotFound - projectCreate: ErrorInternal | ErrorUserBusinessPlanLimitHit | ErrorUserBusinessResourceNotFound | Project - projectDelete: ErrorInternal | ErrorUserBusinessResourceNotFound | ProjectNode - projectUpdate: ErrorInternal | ErrorUserBusinessResourceNotFound | Project - pulseDisable: ErrorInternal | ErrorUserBusinessResourceNotFound | SideEffectConfirmation - pulseEnable: ErrorInternal | ErrorUser | SideEffectConfirmation - serviceKeyCreate: ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyWithValue - serviceKeyDelete: ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyNode - userUpdate: ErrorInternal | ErrorUserBusinessResourceNotFound | User - userUpdateDefaultWorkspace: ErrorInternal | ErrorUserBusinessResourceNotFound | User - workspaceCreate: ErrorInternal | Workspace - workspaceDelete: - | ErrorInternal - | ErrorUserBusinessDeleteWorkspaceOnPaidPlan - | ErrorUserBusinessResourceNotFound - | WorkspaceNode - workspaceMembershipCreate: - | ErrorInternal - | ErrorUserBusinessResourceNotFound - | ErrorUserBusinessUserAlreadyMemberOfOrganization - | WorkspaceMembership - workspaceMembershipDelete: ErrorInternal | ErrorUserBusinessResourceNotFound | WorkspaceMembershipNode - workspacePlanSubscriptionChange: ErrorInternal | ErrorUserBusinessResourceNotFound | PlanSubscription - workspaceUpdate: ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace - workspaceUpdateBillingAddress: ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace - workspaceUpdateBillingEmail: ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace -} +export namespace Root { + export interface Mutation { + 'accelerateCachePurge': + | Object.ErrorInternal + | Object.SideEffectConfirmation + 'accelerateDisable': + | Object.ErrorInternal + | Object.SideEffectConfirmation + 'accelerateEnable': + | Object.ErrorInternal + | Object.SideEffectConfirmation + 'databaseLinkCreate': + | Object.DatabaseLink + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + 'databaseLinkDelete': + | Object.DatabaseLinkNode + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + 'databaseLinkUpdate': + | Object.DatabaseLink + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + 'environmentCreate': + | Object.Environment + | Object.ErrorInternal + | Object.ErrorUserBusinessPlanLimitHit + | Object.ErrorUserBusinessResourceNotFound + 'environmentDelete': + | Object.Environment + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + 'environmentUpdate': + | Object.Environment + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + 'projectCreate': + | Object.ErrorInternal + | Object.ErrorUserBusinessPlanLimitHit + | Object.ErrorUserBusinessResourceNotFound + | Object.Project + 'projectDelete': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ProjectNode + 'projectUpdate': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.Project + 'pulseDisable': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.SideEffectConfirmation + 'pulseEnable': + | Object.ErrorInternal + | Object.ErrorUser + | Object.SideEffectConfirmation + 'serviceKeyCreate': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ServiceKeyWithValue + 'serviceKeyDelete': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ServiceKeyNode + 'userUpdate': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.User + 'userUpdateDefaultWorkspace': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.User + 'workspaceCreate': + | Object.ErrorInternal + | Object.Workspace + 'workspaceDelete': + | Object.ErrorInternal + | Object.ErrorUserBusinessDeleteWorkspaceOnPaidPlan + | Object.ErrorUserBusinessResourceNotFound + | Object.WorkspaceNode + 'workspaceMembershipCreate': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ErrorUserBusinessUserAlreadyMemberOfOrganization + | Object.WorkspaceMembership + 'workspaceMembershipDelete': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.WorkspaceMembershipNode + 'workspacePlanSubscriptionChange': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.PlanSubscription + 'workspaceUpdate': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.Workspace + 'workspaceUpdateBillingAddress': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.Workspace + 'workspaceUpdateBillingEmail': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.Workspace + } -interface Query { - environment: - | Environment - | ErrorInternal - | ErrorUserBusinessNotAuthorized - | ErrorUserBusinessResourceNotFound - me: Me - plan: ErrorInternal | ErrorUserBusinessResourceNotFound | ErrorUserInput | Plan - project: ErrorInternal | ErrorUserBusinessNotAuthorized | ErrorUserBusinessResourceNotFound | Project - serviceKeys: Array - system: System - user: User - workspace: ErrorInternal | ErrorUserBusinessNotAuthorized | ErrorUserBusinessResourceNotFound | Workspace + export interface Query { + 'environment': + | Object.Environment + | Object.ErrorInternal + | Object.ErrorUserBusinessNotAuthorized + | Object.ErrorUserBusinessResourceNotFound + 'me': Object.Me + 'plan': + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ErrorUserInput + | Object.Plan + 'project': + | Object.ErrorInternal + | Object.ErrorUserBusinessNotAuthorized + | Object.ErrorUserBusinessResourceNotFound + | Object.Project + 'serviceKeys': Array + 'system': Object.System + 'user': Object.User + 'workspace': + | Object.ErrorInternal + | Object.ErrorUserBusinessNotAuthorized + | Object.ErrorUserBusinessResourceNotFound + | Object.Workspace + } } // ------------------------------------------------------------ // // Enum // // ------------------------------------------------------------ // -type CountryCode = - | 'AD' - | 'AE' - | 'AF' - | 'AG' - | 'AI' - | 'AL' - | 'AM' - | 'AO' - | 'AQ' - | 'AR' - | 'AS' - | 'AT' - | 'AU' - | 'AW' - | 'AX' - | 'AZ' - | 'BA' - | 'BB' - | 'BD' - | 'BE' - | 'BF' - | 'BG' - | 'BH' - | 'BI' - | 'BJ' - | 'BL' - | 'BM' - | 'BN' - | 'BO' - | 'BQ' - | 'BR' - | 'BS' - | 'BT' - | 'BV' - | 'BW' - | 'BY' - | 'BZ' - | 'CA' - | 'CC' - | 'CD' - | 'CF' - | 'CG' - | 'CH' - | 'CI' - | 'CK' - | 'CL' - | 'CM' - | 'CN' - | 'CO' - | 'CR' - | 'CU' - | 'CV' - | 'CW' - | 'CX' - | 'CY' - | 'CZ' - | 'DE' - | 'DJ' - | 'DK' - | 'DM' - | 'DO' - | 'DZ' - | 'EC' - | 'EE' - | 'EG' - | 'EH' - | 'ER' - | 'ES' - | 'ET' - | 'FI' - | 'FJ' - | 'FK' - | 'FM' - | 'FO' - | 'FR' - | 'GA' - | 'GB' - | 'GD' - | 'GE' - | 'GF' - | 'GG' - | 'GH' - | 'GI' - | 'GL' - | 'GM' - | 'GN' - | 'GP' - | 'GQ' - | 'GR' - | 'GS' - | 'GT' - | 'GU' - | 'GW' - | 'GY' - | 'HK' - | 'HM' - | 'HN' - | 'HR' - | 'HT' - | 'HU' - | 'ID' - | 'IE' - | 'IL' - | 'IM' - | 'IN' - | 'IO' - | 'IQ' - | 'IR' - | 'IS' - | 'IT' - | 'JE' - | 'JM' - | 'JO' - | 'JP' - | 'KE' - | 'KG' - | 'KH' - | 'KI' - | 'KM' - | 'KN' - | 'KP' - | 'KR' - | 'KW' - | 'KY' - | 'KZ' - | 'LA' - | 'LB' - | 'LC' - | 'LI' - | 'LK' - | 'LR' - | 'LS' - | 'LT' - | 'LU' - | 'LV' - | 'LY' - | 'MA' - | 'MC' - | 'MD' - | 'ME' - | 'MF' - | 'MG' - | 'MH' - | 'MK' - | 'ML' - | 'MM' - | 'MN' - | 'MO' - | 'MP' - | 'MQ' - | 'MR' - | 'MS' - | 'MT' - | 'MU' - | 'MV' - | 'MW' - | 'MX' - | 'MY' - | 'MZ' - | 'NA' - | 'NC' - | 'NE' - | 'NF' - | 'NG' - | 'NI' - | 'NL' - | 'NO' - | 'NP' - | 'NR' - | 'NU' - | 'NZ' - | 'OM' - | 'PA' - | 'PE' - | 'PF' - | 'PG' - | 'PH' - | 'PK' - | 'PL' - | 'PM' - | 'PN' - | 'PR' - | 'PS' - | 'PT' - | 'PW' - | 'PY' - | 'QA' - | 'RE' - | 'RO' - | 'RS' - | 'RU' - | 'RW' - | 'SA' - | 'SB' - | 'SC' - | 'SD' - | 'SE' - | 'SG' - | 'SH' - | 'SI' - | 'SJ' - | 'SK' - | 'SL' - | 'SM' - | 'SN' - | 'SO' - | 'SR' - | 'SS' - | 'ST' - | 'SV' - | 'SX' - | 'SY' - | 'SZ' - | 'TC' - | 'TD' - | 'TF' - | 'TG' - | 'TH' - | 'TJ' - | 'TK' - | 'TL' - | 'TM' - | 'TN' - | 'TO' - | 'TR' - | 'TT' - | 'TV' - | 'TW' - | 'TZ' - | 'UA' - | 'UG' - | 'UM' - | 'US' - | 'UY' - | 'UZ' - | 'VA' - | 'VC' - | 'VE' - | 'VG' - | 'VI' - | 'VN' - | 'VU' - | 'WF' - | 'WS' - | 'YE' - | 'YT' - | 'ZA' - | 'ZM' - | 'ZW' - -type EnvironmentAccelerateUsageTimeWindowInput = 'last6h' | 'last7d' | 'last24h' | 'last30d' | 'last30m' - -type FeatureHandle = - | 'accelerateEgress' - | 'acceleratePurgeCache' - | 'accelerateQuery' - | 'access' - | 'createProject' - | 'organizationRole' - | 'platformSupport' - -type FeatureResourceAggregationValueResolverType = 'count' - -type MetricUnit = 'ms' | 'percent' - -type NumberPredicateFnType = - | 'NumberPredicateFnEQ' - | 'NumberPredicateFnGT' - | 'NumberPredicateFnGTE' - | 'NumberPredicateFnLT' - | 'NumberPredicateFnLTE' - -type Order = 'asc' | 'desc' - -type PaymentMethodCardBrand = - | 'amex' - | 'diners' - | 'discover' - | 'eftpos_au' - | 'jcb' - | 'mastercard' - | 'unionpay' - | 'unknown' - | 'visa' - -type PreviousDateHandle = 'last6h' | 'last7d' | 'last24h' | 'last30d' | 'last30m' | 'startOfCycle' - -type ResourceType = 'Project' | 'Workspace' - -type StorageUnit = 'bytes' - -type WorkspaceRole = 'accountant' | 'admin' | 'developer' | 'viewer' +export namespace Enum { + export type CountryCode = + | 'AD' + | 'AE' + | 'AF' + | 'AG' + | 'AI' + | 'AL' + | 'AM' + | 'AO' + | 'AQ' + | 'AR' + | 'AS' + | 'AT' + | 'AU' + | 'AW' + | 'AX' + | 'AZ' + | 'BA' + | 'BB' + | 'BD' + | 'BE' + | 'BF' + | 'BG' + | 'BH' + | 'BI' + | 'BJ' + | 'BL' + | 'BM' + | 'BN' + | 'BO' + | 'BQ' + | 'BR' + | 'BS' + | 'BT' + | 'BV' + | 'BW' + | 'BY' + | 'BZ' + | 'CA' + | 'CC' + | 'CD' + | 'CF' + | 'CG' + | 'CH' + | 'CI' + | 'CK' + | 'CL' + | 'CM' + | 'CN' + | 'CO' + | 'CR' + | 'CU' + | 'CV' + | 'CW' + | 'CX' + | 'CY' + | 'CZ' + | 'DE' + | 'DJ' + | 'DK' + | 'DM' + | 'DO' + | 'DZ' + | 'EC' + | 'EE' + | 'EG' + | 'EH' + | 'ER' + | 'ES' + | 'ET' + | 'FI' + | 'FJ' + | 'FK' + | 'FM' + | 'FO' + | 'FR' + | 'GA' + | 'GB' + | 'GD' + | 'GE' + | 'GF' + | 'GG' + | 'GH' + | 'GI' + | 'GL' + | 'GM' + | 'GN' + | 'GP' + | 'GQ' + | 'GR' + | 'GS' + | 'GT' + | 'GU' + | 'GW' + | 'GY' + | 'HK' + | 'HM' + | 'HN' + | 'HR' + | 'HT' + | 'HU' + | 'ID' + | 'IE' + | 'IL' + | 'IM' + | 'IN' + | 'IO' + | 'IQ' + | 'IR' + | 'IS' + | 'IT' + | 'JE' + | 'JM' + | 'JO' + | 'JP' + | 'KE' + | 'KG' + | 'KH' + | 'KI' + | 'KM' + | 'KN' + | 'KP' + | 'KR' + | 'KW' + | 'KY' + | 'KZ' + | 'LA' + | 'LB' + | 'LC' + | 'LI' + | 'LK' + | 'LR' + | 'LS' + | 'LT' + | 'LU' + | 'LV' + | 'LY' + | 'MA' + | 'MC' + | 'MD' + | 'ME' + | 'MF' + | 'MG' + | 'MH' + | 'MK' + | 'ML' + | 'MM' + | 'MN' + | 'MO' + | 'MP' + | 'MQ' + | 'MR' + | 'MS' + | 'MT' + | 'MU' + | 'MV' + | 'MW' + | 'MX' + | 'MY' + | 'MZ' + | 'NA' + | 'NC' + | 'NE' + | 'NF' + | 'NG' + | 'NI' + | 'NL' + | 'NO' + | 'NP' + | 'NR' + | 'NU' + | 'NZ' + | 'OM' + | 'PA' + | 'PE' + | 'PF' + | 'PG' + | 'PH' + | 'PK' + | 'PL' + | 'PM' + | 'PN' + | 'PR' + | 'PS' + | 'PT' + | 'PW' + | 'PY' + | 'QA' + | 'RE' + | 'RO' + | 'RS' + | 'RU' + | 'RW' + | 'SA' + | 'SB' + | 'SC' + | 'SD' + | 'SE' + | 'SG' + | 'SH' + | 'SI' + | 'SJ' + | 'SK' + | 'SL' + | 'SM' + | 'SN' + | 'SO' + | 'SR' + | 'SS' + | 'ST' + | 'SV' + | 'SX' + | 'SY' + | 'SZ' + | 'TC' + | 'TD' + | 'TF' + | 'TG' + | 'TH' + | 'TJ' + | 'TK' + | 'TL' + | 'TM' + | 'TN' + | 'TO' + | 'TR' + | 'TT' + | 'TV' + | 'TW' + | 'TZ' + | 'UA' + | 'UG' + | 'UM' + | 'US' + | 'UY' + | 'UZ' + | 'VA' + | 'VC' + | 'VE' + | 'VG' + | 'VI' + | 'VN' + | 'VU' + | 'WF' + | 'WS' + | 'YE' + | 'YT' + | 'ZA' + | 'ZM' + | 'ZW' + + export type EnvironmentAccelerateUsageTimeWindowInput = + | 'last6h' + | 'last7d' + | 'last24h' + | 'last30d' + | 'last30m' + + export type FeatureHandle = + | 'accelerateEgress' + | 'acceleratePurgeCache' + | 'accelerateQuery' + | 'access' + | 'createProject' + | 'organizationRole' + | 'platformSupport' + + export type FeatureResourceAggregationValueResolverType = 'count' + + export type MetricUnit = + | 'ms' + | 'percent' + + export type NumberPredicateFnType = + | 'NumberPredicateFnEQ' + | 'NumberPredicateFnGT' + | 'NumberPredicateFnGTE' + | 'NumberPredicateFnLT' + | 'NumberPredicateFnLTE' + + export type Order = + | 'asc' + | 'desc' + + export type PaymentMethodCardBrand = + | 'amex' + | 'diners' + | 'discover' + | 'eftpos_au' + | 'jcb' + | 'mastercard' + | 'unionpay' + | 'unknown' + | 'visa' + + export type PreviousDateHandle = + | 'last6h' + | 'last7d' + | 'last24h' + | 'last30d' + | 'last30m' + | 'startOfCycle' + + export type ResourceType = + | 'Project' + | 'Workspace' + + export type StorageUnit = 'bytes' + + export type WorkspaceRole = + | 'accountant' + | 'admin' + | 'developer' + | 'viewer' +} // ------------------------------------------------------------ // // InputObject // // ------------------------------------------------------------ // -interface MutationAccelerateCachePurgeInput { - environmentId: $.Scalars['ID'] -} +export namespace InputObject { + export interface MutationAccelerateCachePurgeInput { + 'environmentId': $.Scalars['ID'] + } -interface MutationAccelerateDisableInput { - environmentId: $.Scalars['ID'] -} + export interface MutationAccelerateDisableInput { + 'environmentId': $.Scalars['ID'] + } -interface MutationAccelerateEnableInput { - databaseLinkId: $.Scalars['ID'] -} + export interface MutationAccelerateEnableInput { + 'databaseLinkId': $.Scalars['ID'] + } -interface MutationDatabaseLinkCreateInput { - connectionString: $.Scalars['String'] - displayName: $.Scalars['String'] | null - environmentId: $.Scalars['ID'] - regionId: $.Scalars['String'] | null -} + export interface MutationDatabaseLinkCreateInput { + 'connectionString': $.Scalars['String'] + 'displayName': $.Scalars['String'] | null + 'environmentId': $.Scalars['ID'] + 'regionId': $.Scalars['String'] | null + } -interface MutationDatabaseLinkDeleteInput { - id: $.Scalars['ID'] -} + export interface MutationDatabaseLinkDeleteInput { + 'id': $.Scalars['ID'] + } -interface MutationDatabaseLinkUpdateInput { - connectionString: $.Scalars['String'] - id: $.Scalars['ID'] - regionId: $.Scalars['String'] -} + export interface MutationDatabaseLinkUpdateInput { + 'connectionString': $.Scalars['String'] + 'id': $.Scalars['ID'] + 'regionId': $.Scalars['String'] + } -interface MutationEnvironmentCreateInput { - displayName: $.Scalars['String'] | null - isDefault: $.Scalars['Boolean'] | null - projectId: $.Scalars['ID'] -} + export interface MutationEnvironmentCreateInput { + 'displayName': $.Scalars['String'] | null + 'isDefault': $.Scalars['Boolean'] | null + 'projectId': $.Scalars['ID'] + } -interface MutationEnvironmentDeleteInput { - id: $.Scalars['ID'] -} + export interface MutationEnvironmentDeleteInput { + 'id': $.Scalars['ID'] + } -interface MutationEnvironmentUpdateInput { - displayName: $.Scalars['String'] | null - id: $.Scalars['ID'] - isDefault: $.Scalars['Boolean'] | null -} + export interface MutationEnvironmentUpdateInput { + 'displayName': $.Scalars['String'] | null + 'id': $.Scalars['ID'] + 'isDefault': $.Scalars['Boolean'] | null + } -interface MutationProjectCreateInput { - displayName: $.Scalars['String'] | null - workspaceId: $.Scalars['ID'] -} + export interface MutationProjectCreateInput { + 'displayName': $.Scalars['String'] | null + 'workspaceId': $.Scalars['ID'] + } -interface MutationProjectDeleteInput { - id: $.Scalars['ID'] -} + export interface MutationProjectDeleteInput { + 'id': $.Scalars['ID'] + } -interface MutationProjectUpdateInput { - displayName: $.Scalars['String'] | null - id: $.Scalars['ID'] -} + export interface MutationProjectUpdateInput { + 'displayName': $.Scalars['String'] | null + 'id': $.Scalars['ID'] + } -interface MutationPulseDisableInput { - environmentId: $.Scalars['String'] -} + export interface MutationPulseDisableInput { + 'environmentId': $.Scalars['String'] + } -interface MutationPulseEnableInput { - databaseLinkId: $.Scalars['String'] -} + export interface MutationPulseEnableInput { + 'databaseLinkId': $.Scalars['String'] + } -interface MutationServiceKeyCreateInput { - displayName: $.Scalars['String'] | null - environmentId: $.Scalars['ID'] -} + export interface MutationServiceKeyCreateInput { + 'displayName': $.Scalars['String'] | null + 'environmentId': $.Scalars['ID'] + } -interface MutationServiceKeyDeleteInput { - id: $.Scalars['String'] -} + export interface MutationServiceKeyDeleteInput { + 'id': $.Scalars['String'] + } -interface MutationUserUpdateDefaultWorkspaceInput { - workspaceId: $.Scalars['ID'] -} + export interface MutationUserUpdateDefaultWorkspaceInput { + 'workspaceId': $.Scalars['ID'] + } -interface MutationUserUpdateInput { - displayName: $.Scalars['String'] | null - id: $.Scalars['ID'] -} + export interface MutationUserUpdateInput { + 'displayName': $.Scalars['String'] | null + 'id': $.Scalars['ID'] + } -interface MutationWorkspaceCreateInput { - displayName: $.Scalars['String'] | null -} + export interface MutationWorkspaceCreateInput { + 'displayName': $.Scalars['String'] | null + } -interface MutationWorkspaceDeleteInput { - id: $.Scalars['ID'] -} + export interface MutationWorkspaceDeleteInput { + 'id': $.Scalars['ID'] + } -interface MutationWorkspaceMembershipCreateInput { - email: $.Scalars['String'] - role: WorkspaceRole - workspaceId: $.Scalars['ID'] -} + export interface MutationWorkspaceMembershipCreateInput { + 'email': $.Scalars['String'] + 'role': Enum.WorkspaceRole + 'workspaceId': $.Scalars['ID'] + } -interface MutationWorkspaceMembershipDeleteInput { - id: $.Scalars['ID'] -} + export interface MutationWorkspaceMembershipDeleteInput { + 'id': $.Scalars['ID'] + } -interface MutationWorkspacePlanSubscriptionChangeInput { - targetPlanId: $.Scalars['ID'] - workspaceId: $.Scalars['ID'] -} + export interface MutationWorkspacePlanSubscriptionChangeInput { + 'targetPlanId': $.Scalars['ID'] + 'workspaceId': $.Scalars['ID'] + } -interface MutationWorkspaceUpdateBillingAddressInput { - address: PhysicalAddressInput - id: $.Scalars['ID'] -} + export interface MutationWorkspaceUpdateBillingAddressInput { + 'address': InputObject.PhysicalAddressInput + 'id': $.Scalars['ID'] + } -interface MutationWorkspaceUpdateBillingEmailInput { - email: $.Scalars['String'] - id: $.Scalars['ID'] -} + export interface MutationWorkspaceUpdateBillingEmailInput { + 'email': $.Scalars['String'] + 'id': $.Scalars['ID'] + } -interface MutationWorkspaceUpdateInput { - displayName: $.Scalars['String'] | null - id: $.Scalars['ID'] -} + export interface MutationWorkspaceUpdateInput { + 'displayName': $.Scalars['String'] | null + 'id': $.Scalars['ID'] + } -interface PhysicalAddressInput { - addressLine1: $.Scalars['String'] | null - addressLine2: $.Scalars['String'] | null - city: $.Scalars['String'] | null - country: CountryCode | null - postalCodeOrZIP: $.Scalars['String'] | null - region: $.Scalars['String'] | null -} + export interface PhysicalAddressInput { + 'addressLine1': $.Scalars['String'] | null + 'addressLine2': $.Scalars['String'] | null + 'city': $.Scalars['String'] | null + 'country': Enum.CountryCode | null + 'postalCodeOrZIP': $.Scalars['String'] | null + 'region': $.Scalars['String'] | null + } -interface TimeIntervalInput { - fromDate: $.Scalars['Date'] | null - fromDateHandle: PreviousDateHandle | null - toDate: $.Scalars['Date'] | null -} + export interface TimeIntervalInput { + 'fromDate': $.Scalars['Date'] | null + 'fromDateHandle': Enum.PreviousDateHandle | null + 'toDate': $.Scalars['Date'] | null + } -interface WorkspaceOrderBy { - displayName: Order | null + export interface WorkspaceOrderBy { + 'displayName': Enum.Order | null + } } // ------------------------------------------------------------ // // Interface // // ------------------------------------------------------------ // -interface Error { - message: $.Scalars['String'] -} +export namespace Interface { + export interface Error { + 'message': $.Scalars['String'] + } -interface Feature { - displayName: $.Scalars['String'] | null - handle: FeatureHandle - id: $.Scalars['ID'] - stripeProductId: $.Scalars['String'] -} + export interface Feature { + 'displayName': $.Scalars['String'] | null + 'handle': Enum.FeatureHandle + 'id': $.Scalars['ID'] + 'stripeProductId': $.Scalars['String'] + } -interface Node { - id: $.Scalars['String'] -} + export interface Node { + 'id': $.Scalars['String'] + } -interface Offer { - context: Plan | PlanSubscription - id: $.Scalars['ID'] - price: PriceConstant | PriceTiered | null -} + export interface Offer { + 'context': + | Object.Plan + | Object.PlanSubscription + 'id': $.Scalars['ID'] + 'price': + | Object.PriceConstant + | Object.PriceTiered + | null + } -interface PriceI { - id: $.Scalars['String'] - stripePriceId: $.Scalars['ID'] -} + export interface PriceI { + 'id': $.Scalars['String'] + 'stripePriceId': $.Scalars['ID'] + } -interface ProductStatus { - enabled: $.Scalars['Boolean'] + export interface ProductStatus { + 'enabled': $.Scalars['Boolean'] + } } // ------------------------------------------------------------ // // Object // // ------------------------------------------------------------ // -interface AccelerateStatusDisabled { - enabled: $.Scalars['Boolean'] -} +export namespace Object { + export interface AccelerateStatusDisabled { + 'enabled': $.Scalars['Boolean'] + } -interface AccelerateStatusEnabled { - enabled: $.Scalars['Boolean'] -} + export interface AccelerateStatusEnabled { + 'enabled': $.Scalars['Boolean'] + } -interface Count { - number: $.Scalars['Int'] -} + export interface Count { + 'number': $.Scalars['Int'] + } -interface DatabaseLink { - connectionStringHint: $.Scalars['String'] - id: $.Scalars['ID'] - protocol: $.Scalars['String'] - region: $.Scalars['String'] | null -} + export interface DatabaseLink { + 'connectionStringHint': $.Scalars['String'] + 'id': $.Scalars['ID'] + 'protocol': $.Scalars['String'] + 'region': $.Scalars['String'] | null + } -interface DatabaseLinkNode { - connectionStringHint: $.Scalars['ID'] - displayName: $.Scalars['String'] - id: $.Scalars['String'] -} + export interface DatabaseLinkNode { + 'connectionStringHint': $.Scalars['ID'] + 'displayName': $.Scalars['String'] + 'id': $.Scalars['String'] + } -interface Environment { - accelerate: EnvironmentAccelerate - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - id: $.Scalars['ID'] - isDefault: $.Scalars['Boolean'] - project: Project - pulse: EnvironmentPulse - serviceKeys: Array - tenantId: $.Scalars['ID'] -} + export interface Environment { + 'accelerate': Object.EnvironmentAccelerate + 'createdAt': $.Scalars['Date'] + 'displayName': $.Scalars['String'] + 'id': $.Scalars['ID'] + 'isDefault': $.Scalars['Boolean'] + 'project': Object.Project + 'pulse': Object.EnvironmentPulse + 'serviceKeys': Array + 'tenantId': $.Scalars['ID'] + } -interface EnvironmentAccelerate { - databaseLink: DatabaseLink | null - holds: Array - status: AccelerateStatusDisabled | AccelerateStatusEnabled - usage: EnvironmentAccelerateUsage -} + export interface EnvironmentAccelerate { + 'databaseLink': Object.DatabaseLink | null + 'holds': Array + 'status': + | Object.AccelerateStatusDisabled + | Object.AccelerateStatusEnabled + 'usage': Object.EnvironmentAccelerateUsage + } -interface EnvironmentAccelerateTimeSeriesPoints { - queries: EnvironmentAccelerateUsageTimeSeriesPointsQueries - timestamps: Array<$.Scalars['Date']> -} + export interface EnvironmentAccelerateTimeSeriesPoints { + 'queries': Object.EnvironmentAccelerateUsageTimeSeriesPointsQueries + 'timestamps': Array<$.Scalars['Date']> + } -interface EnvironmentAccelerateUsage { - latency: EnvironmentAccelerateUsageLatency - overview: EnvironmentAccelerateUsageOverview - timeInterval: TimeInterval - timeSeries: EnvironmentAccelerateUsageTimeSeries -} + export interface EnvironmentAccelerateUsage { + 'latency': Object.EnvironmentAccelerateUsageLatency + 'overview': Object.EnvironmentAccelerateUsageOverview + 'timeInterval': Object.TimeInterval + 'timeSeries': Object.EnvironmentAccelerateUsageTimeSeries + } -interface EnvironmentAccelerateUsageLatency { - queries: EnvironmentAccelerateUsageLatencyQueries -} + export interface EnvironmentAccelerateUsageLatency { + 'queries': Object.EnvironmentAccelerateUsageLatencyQueries + } -interface EnvironmentAccelerateUsageLatencyQueries { - cached: EnvironmentAccelerateUsageLatencyQuery - origin: EnvironmentAccelerateUsageLatencyQuery -} + export interface EnvironmentAccelerateUsageLatencyQueries { + 'cached': Object.EnvironmentAccelerateUsageLatencyQuery + 'origin': Object.EnvironmentAccelerateUsageLatencyQuery + } -interface EnvironmentAccelerateUsageLatencyQuery { - count: Count - durationAverage: MetricValue - durationPercentiles: Array -} + export interface EnvironmentAccelerateUsageLatencyQuery { + 'count': Object.Count + 'durationAverage': Object.MetricValue + 'durationPercentiles': Array + } -interface EnvironmentAccelerateUsageOverview { - egress: EnvironmentAccelerateUsageOverviewEgress - queries: EnvironmentAccelerateUsageOverviewQueries -} + export interface EnvironmentAccelerateUsageOverview { + 'egress': Object.EnvironmentAccelerateUsageOverviewEgress + 'queries': Object.EnvironmentAccelerateUsageOverviewQueries + } -interface EnvironmentAccelerateUsageOverviewCacheHit { - ratioToMiss: MetricValue -} + export interface EnvironmentAccelerateUsageOverviewCacheHit { + 'ratioToMiss': Object.MetricValue + } -interface EnvironmentAccelerateUsageOverviewEgress { - averageResponseSize: StorageValue - requestsServedFromOrigin: Count - total: StorageValue -} + export interface EnvironmentAccelerateUsageOverviewEgress { + 'averageResponseSize': Object.StorageValue + 'requestsServedFromOrigin': Object.Count + 'total': Object.StorageValue + } -interface EnvironmentAccelerateUsageOverviewQueries { - cacheHit: EnvironmentAccelerateUsageOverviewCacheHit - cacheableCount: Count - totalCount: Count -} + export interface EnvironmentAccelerateUsageOverviewQueries { + 'cacheHit': Object.EnvironmentAccelerateUsageOverviewCacheHit + 'cacheableCount': Object.Count + 'totalCount': Object.Count + } -interface EnvironmentAccelerateUsageTimeSeries { - points: EnvironmentAccelerateTimeSeriesPoints -} + export interface EnvironmentAccelerateUsageTimeSeries { + 'points': Object.EnvironmentAccelerateTimeSeriesPoints + } -interface EnvironmentAccelerateUsageTimeSeriesPointsQueries { - miss: Array | null - none: Array | null - swr: Array | null - ttl: Array | null -} + export interface EnvironmentAccelerateUsageTimeSeriesPointsQueries { + 'miss': Array | null + 'none': Array | null + 'swr': Array | null + 'ttl': Array | null + } -interface EnvironmentAccelerateUsageTimeSeriesPointsQuery { - count: Count - timestamp: $.Scalars['Date'] -} + export interface EnvironmentAccelerateUsageTimeSeriesPointsQuery { + 'count': Object.Count + 'timestamp': $.Scalars['Date'] + } -interface EnvironmentPulse { - databaseLink: DatabaseLink | null - status: PulseStatusDisabled | PulseStatusEnabled -} + export interface EnvironmentPulse { + 'databaseLink': Object.DatabaseLink | null + 'status': + | Object.PulseStatusDisabled + | Object.PulseStatusEnabled + } -interface ErrorInternal { - message: $.Scalars['String'] -} + export interface ErrorInternal { + 'message': $.Scalars['String'] + } -interface ErrorUser { - message: $.Scalars['String'] -} + export interface ErrorUser { + 'message': $.Scalars['String'] + } -interface ErrorUserBusinessDeleteWorkspaceOnPaidPlan { - context: ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext - message: $.Scalars['String'] -} + export interface ErrorUserBusinessDeleteWorkspaceOnPaidPlan { + 'context': Object.ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext + 'message': $.Scalars['String'] + } -interface ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext { - plan: Plan -} + export interface ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext { + 'plan': Object.Plan + } -interface ErrorUserBusinessNotAuthorized { - message: $.Scalars['String'] -} + export interface ErrorUserBusinessNotAuthorized { + 'message': $.Scalars['String'] + } -interface ErrorUserBusinessPlanLimitHit { - context: ErrorUserBusinessPlanLimitHitContext - message: $.Scalars['String'] -} + export interface ErrorUserBusinessPlanLimitHit { + 'context': Object.ErrorUserBusinessPlanLimitHitContext + 'message': $.Scalars['String'] + } -interface ErrorUserBusinessPlanLimitHitContext { - featureHandle: $.Scalars['String'] | null -} + export interface ErrorUserBusinessPlanLimitHitContext { + 'featureHandle': $.Scalars['String'] | null + } -interface ErrorUserBusinessResourceNotFound { - context: ErrorUserBusinessResourceNotFoundContext - message: $.Scalars['String'] -} + export interface ErrorUserBusinessResourceNotFound { + 'context': Object.ErrorUserBusinessResourceNotFoundContext + 'message': $.Scalars['String'] + } -interface ErrorUserBusinessResourceNotFoundContext { - id: $.Scalars['ID'] | null - typeName: ResourceType -} + export interface ErrorUserBusinessResourceNotFoundContext { + 'id': $.Scalars['ID'] | null + 'typeName': Enum.ResourceType + } -interface ErrorUserBusinessUserAlreadyMemberOfOrganization { - context: ErrorUserBusinessUserAlreadyMemberOfOrganizationContext - message: $.Scalars['String'] -} + export interface ErrorUserBusinessUserAlreadyMemberOfOrganization { + 'context': Object.ErrorUserBusinessUserAlreadyMemberOfOrganizationContext + 'message': $.Scalars['String'] + } -interface ErrorUserBusinessUserAlreadyMemberOfOrganizationContext { - user: User - workspace: Workspace -} + export interface ErrorUserBusinessUserAlreadyMemberOfOrganizationContext { + 'user': Object.User + 'workspace': Object.Workspace + } -interface ErrorUserInput { - message: $.Scalars['String'] -} + export interface ErrorUserInput { + 'message': $.Scalars['String'] + } -interface FeatureAbstract { - displayName: $.Scalars['String'] | null - handle: FeatureHandle - id: $.Scalars['ID'] - stripeProductId: $.Scalars['String'] -} + export interface FeatureAbstract { + 'displayName': $.Scalars['String'] | null + 'handle': Enum.FeatureHandle + 'id': $.Scalars['ID'] + 'stripeProductId': $.Scalars['String'] + } -interface FeatureResourceAggregation { - displayName: $.Scalars['String'] | null - handle: FeatureHandle - id: $.Scalars['ID'] - resource: $.Scalars['String'] - scope: $.Scalars['String'] - stripeProductId: $.Scalars['String'] - valueResolver: FeatureResourceAggregationValueResolver -} + export interface FeatureResourceAggregation { + 'displayName': $.Scalars['String'] | null + 'handle': Enum.FeatureHandle + 'id': $.Scalars['ID'] + 'resource': $.Scalars['String'] + 'scope': $.Scalars['String'] + 'stripeProductId': $.Scalars['String'] + 'valueResolver': Object.FeatureResourceAggregationValueResolver + } -interface FeatureResourceAggregationValueResolver { - type: FeatureResourceAggregationValueResolverType -} + export interface FeatureResourceAggregationValueResolver { + 'type': Enum.FeatureResourceAggregationValueResolverType + } -interface FeatureResourceProperty { - displayName: $.Scalars['String'] | null - handle: FeatureHandle - id: $.Scalars['ID'] - resource: $.Scalars['String'] - scope: $.Scalars['String'] - stripeProductId: $.Scalars['String'] - valueResolver: FeatureResourcePropertyValueResolver -} + export interface FeatureResourceProperty { + 'displayName': $.Scalars['String'] | null + 'handle': Enum.FeatureHandle + 'id': $.Scalars['ID'] + 'resource': $.Scalars['String'] + 'scope': $.Scalars['String'] + 'stripeProductId': $.Scalars['String'] + 'valueResolver': Object.FeatureResourcePropertyValueResolver + } -interface FeatureResourcePropertyValueResolver { - field: $.Scalars['String'] - type: FeatureValueTypeBoolean | FeatureValueTypeEnum | FeatureValueTypeNumber | FeatureValueTypeString -} + export interface FeatureResourcePropertyValueResolver { + 'field': $.Scalars['String'] + 'type': + | Object.FeatureValueTypeBoolean + | Object.FeatureValueTypeEnum + | Object.FeatureValueTypeNumber + | Object.FeatureValueTypeString + } -interface FeatureValue { - displayName: $.Scalars['String'] | null - handle: FeatureHandle - id: $.Scalars['ID'] - stripeProductId: $.Scalars['String'] - valueType: FeatureValueTypeBoolean | FeatureValueTypeEnum | FeatureValueTypeNumber | FeatureValueTypeString -} + export interface FeatureValue { + 'displayName': $.Scalars['String'] | null + 'handle': Enum.FeatureHandle + 'id': $.Scalars['ID'] + 'stripeProductId': $.Scalars['String'] + 'valueType': + | Object.FeatureValueTypeBoolean + | Object.FeatureValueTypeEnum + | Object.FeatureValueTypeNumber + | Object.FeatureValueTypeString + } -interface FeatureValueTypeBoolean { - displayName: $.Scalars['String'] -} + export interface FeatureValueTypeBoolean { + 'displayName': $.Scalars['String'] + } -interface FeatureValueTypeEnum { - displayName: $.Scalars['String'] - members: Array -} + export interface FeatureValueTypeEnum { + 'displayName': $.Scalars['String'] + 'members': Array + } -interface FeatureValueTypeEnumMember { - description: $.Scalars['String'] | null - value: $.Scalars['String'] -} + export interface FeatureValueTypeEnumMember { + 'description': $.Scalars['String'] | null + 'value': $.Scalars['String'] + } -interface FeatureValueTypeNumber { - displayName: $.Scalars['String'] -} + export interface FeatureValueTypeNumber { + 'displayName': $.Scalars['String'] + } -interface FeatureValueTypeString { - displayName: $.Scalars['String'] -} + export interface FeatureValueTypeString { + 'displayName': $.Scalars['String'] + } -interface LimitEnum { - allowed: Array<$.Scalars['String']> -} + export interface LimitEnum { + 'allowed': Array<$.Scalars['String']> + } -interface LimitNumber { - amount: $.Scalars['Int'] - type: NumberPredicateFnType -} + export interface LimitNumber { + 'amount': $.Scalars['Int'] + 'type': Enum.NumberPredicateFnType + } -interface Me { - user: User - workspaces: Array -} + export interface Me { + 'user': Object.User + 'workspaces': Array + } -interface MetricValue { - number: $.Scalars['Float'] - unit: MetricUnit | null -} + export interface MetricValue { + 'number': $.Scalars['Float'] + 'unit': Enum.MetricUnit | null + } -interface OfferAbstract { - context: Plan | PlanSubscription - feature: FeatureAbstract - id: $.Scalars['ID'] - price: PriceConstant | PriceTiered | null -} + export interface OfferAbstract { + 'context': + | Object.Plan + | Object.PlanSubscription + 'feature': Object.FeatureAbstract + 'id': $.Scalars['ID'] + 'price': + | Object.PriceConstant + | Object.PriceTiered + | null + } -interface OfferResourceAggregation { - context: Plan | PlanSubscription - feature: FeatureResourceAggregation - id: $.Scalars['ID'] - limit: LimitEnum | LimitNumber | null - price: PriceConstant | PriceTiered | null - timeInterval: OfferTimeIntervalCycle | OfferTimeIntervalPrevious | null -} + export interface OfferResourceAggregation { + 'context': + | Object.Plan + | Object.PlanSubscription + 'feature': Object.FeatureResourceAggregation + 'id': $.Scalars['ID'] + 'limit': + | Object.LimitEnum + | Object.LimitNumber + | null + 'price': + | Object.PriceConstant + | Object.PriceTiered + | null + 'timeInterval': + | Object.OfferTimeIntervalCycle + | Object.OfferTimeIntervalPrevious + | null + } -interface OfferResourceProperty { - context: Plan | PlanSubscription - feature: FeatureResourceProperty - id: $.Scalars['ID'] - limit: LimitEnum | LimitNumber | null - price: PriceConstant | PriceTiered | null - timeInterval: OfferTimeIntervalCycle | OfferTimeIntervalPrevious | null -} + export interface OfferResourceProperty { + 'context': + | Object.Plan + | Object.PlanSubscription + 'feature': Object.FeatureResourceProperty + 'id': $.Scalars['ID'] + 'limit': + | Object.LimitEnum + | Object.LimitNumber + | null + 'price': + | Object.PriceConstant + | Object.PriceTiered + | null + 'timeInterval': + | Object.OfferTimeIntervalCycle + | Object.OfferTimeIntervalPrevious + | null + } -interface OfferTimeIntervalCycle { - ok: $.Scalars['Boolean'] -} + export interface OfferTimeIntervalCycle { + 'ok': $.Scalars['Boolean'] + } -interface OfferTimeIntervalPrevious { - milliseconds: $.Scalars['Int'] -} + export interface OfferTimeIntervalPrevious { + 'milliseconds': $.Scalars['Int'] + } -interface OfferValue { - context: Plan | PlanSubscription - feature: FeatureValue - id: $.Scalars['ID'] - limit: LimitEnum | LimitNumber | null - price: PriceConstant | PriceTiered | null - value: $.Scalars['String'] -} + export interface OfferValue { + 'context': + | Object.Plan + | Object.PlanSubscription + 'feature': Object.FeatureValue + 'id': $.Scalars['ID'] + 'limit': + | Object.LimitEnum + | Object.LimitNumber + | null + 'price': + | Object.PriceConstant + | Object.PriceTiered + | null + 'value': $.Scalars['String'] + } -interface PaymentMethod { - card: PaymentMethodCard - id: $.Scalars['ID'] - isDefault: $.Scalars['Boolean'] -} + export interface PaymentMethod { + 'card': Object.PaymentMethodCard + 'id': $.Scalars['ID'] + 'isDefault': $.Scalars['Boolean'] + } -interface PaymentMethodCard { - brand: PaymentMethodCardBrand - expiryMonth: $.Scalars['Int'] - expiryYear: $.Scalars['Int'] - id: $.Scalars['ID'] - last4: $.Scalars['String'] -} + export interface PaymentMethodCard { + 'brand': Enum.PaymentMethodCardBrand + 'expiryMonth': $.Scalars['Int'] + 'expiryYear': $.Scalars['Int'] + 'id': $.Scalars['ID'] + 'last4': $.Scalars['String'] + } -interface Percentile { - percentile: $.Scalars['Int'] - value: MetricValue -} + export interface Percentile { + 'percentile': $.Scalars['Int'] + 'value': Object.MetricValue + } -interface PhysicalAddress { - addressLine1: $.Scalars['String'] | null - addressLine2: $.Scalars['String'] | null - city: $.Scalars['String'] | null - country: $.Scalars['String'] | null - postalCodeOrZIP: $.Scalars['String'] | null - region: $.Scalars['String'] | null -} + export interface PhysicalAddress { + 'addressLine1': $.Scalars['String'] | null + 'addressLine2': $.Scalars['String'] | null + 'city': $.Scalars['String'] | null + 'country': $.Scalars['String'] | null + 'postalCodeOrZIP': $.Scalars['String'] | null + 'region': $.Scalars['String'] | null + } -interface Plan { - displayName: $.Scalars['String'] - handle: $.Scalars['String'] - id: $.Scalars['ID'] - isDefault: $.Scalars['Boolean'] - isFree: $.Scalars['Boolean'] - offers: PlanOffers - power: $.Scalars['Int'] - selectable: $.Scalars['Boolean'] - version: $.Scalars['Int'] - versionIsLatest: $.Scalars['Boolean'] - versions: PlanVersions -} + export interface Plan { + 'displayName': $.Scalars['String'] + 'handle': $.Scalars['String'] + 'id': $.Scalars['ID'] + 'isDefault': $.Scalars['Boolean'] + 'isFree': $.Scalars['Boolean'] + 'offers': Object.PlanOffers + 'power': $.Scalars['Int'] + 'selectable': $.Scalars['Boolean'] + 'version': $.Scalars['Int'] + 'versionIsLatest': $.Scalars['Boolean'] + 'versions': Object.PlanVersions + } -interface PlanOffers { - accelerate: PlanOffersAccelerate - conductor: PlanOffersConductor - platform: PlanOffersPlatform -} + export interface PlanOffers { + 'accelerate': Object.PlanOffersAccelerate + 'conductor': Object.PlanOffersConductor + 'platform': Object.PlanOffersPlatform + } -interface PlanOffersAccelerate { - egress: OfferResourceProperty - purgeCache: OfferResourceAggregation - query: OfferResourceAggregation -} + export interface PlanOffersAccelerate { + 'egress': Object.OfferResourceProperty + 'purgeCache': Object.OfferResourceAggregation + 'query': Object.OfferResourceAggregation + } -interface PlanOffersConductor { - createProject: OfferResourceAggregation - organizationRole: OfferResourceProperty -} + export interface PlanOffersConductor { + 'createProject': Object.OfferResourceAggregation + 'organizationRole': Object.OfferResourceProperty + } -interface PlanOffersPlatform { - access: OfferAbstract - support: OfferValue -} + export interface PlanOffersPlatform { + 'access': Object.OfferAbstract + 'support': Object.OfferValue + } -interface PlanSubscription { - createdAt: $.Scalars['Date'] - id: $.Scalars['ID'] - plan: Plan - stripeSubscriptionId: $.Scalars['String'] | null - stripeSubscriptionLineItems: Array - workspace: Workspace -} + export interface PlanSubscription { + 'createdAt': $.Scalars['Date'] + 'id': $.Scalars['ID'] + 'plan': Object.Plan + 'stripeSubscriptionId': $.Scalars['String'] | null + 'stripeSubscriptionLineItems': Array + 'workspace': Object.Workspace + } -interface PlanVersions { - isLatest: $.Scalars['Boolean'] - next: Array - previous: Array -} + export interface PlanVersions { + 'isLatest': $.Scalars['Boolean'] + 'next': Array + 'previous': Array + } -interface PriceConstant { - cents: $.Scalars['Int'] - id: $.Scalars['String'] - stripePriceId: $.Scalars['ID'] -} + export interface PriceConstant { + 'cents': $.Scalars['Int'] + 'id': $.Scalars['String'] + 'stripePriceId': $.Scalars['ID'] + } -interface PriceTiered { - id: $.Scalars['String'] - stripePriceId: $.Scalars['ID'] - tiers: Array -} + export interface PriceTiered { + 'id': $.Scalars['String'] + 'stripePriceId': $.Scalars['ID'] + 'tiers': Array + } -interface PriceTieredTier { - cents: $.Scalars['Float'] - from: $.Scalars['Int'] - to: $.Scalars['Int'] | null -} + export interface PriceTieredTier { + 'cents': $.Scalars['Float'] + 'from': $.Scalars['Int'] + 'to': $.Scalars['Int'] | null + } -interface ProductHold { - createdAt: $.Scalars['Int'] - expiresAt: $.Scalars['Int'] - reason: $.Scalars['String'] -} + export interface ProductHold { + 'createdAt': $.Scalars['Int'] + 'expiresAt': $.Scalars['Int'] + 'reason': $.Scalars['String'] + } -interface Project { - accelerate: EnvironmentAccelerate - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - environments: Array - id: $.Scalars['ID'] - pulse: EnvironmentPulse - workspace: Workspace -} + export interface Project { + 'accelerate': Object.EnvironmentAccelerate + 'createdAt': $.Scalars['Date'] + 'displayName': $.Scalars['String'] + 'environments': Array + 'id': $.Scalars['ID'] + 'pulse': Object.EnvironmentPulse + 'workspace': Object.Workspace + } -interface ProjectNode { - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - id: $.Scalars['String'] - workspaceId: $.Scalars['ID'] -} + export interface ProjectNode { + 'createdAt': $.Scalars['Date'] + 'displayName': $.Scalars['String'] + 'id': $.Scalars['String'] + 'workspaceId': $.Scalars['ID'] + } -interface PulseStatusDisabled { - enabled: $.Scalars['Boolean'] -} + export interface PulseStatusDisabled { + 'enabled': $.Scalars['Boolean'] + } -interface PulseStatusEnabled { - enabled: $.Scalars['Boolean'] - error: $.Scalars['String'] | null -} + export interface PulseStatusEnabled { + 'enabled': $.Scalars['Boolean'] + 'error': $.Scalars['String'] | null + } -interface ServiceKey { - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - id: $.Scalars['ID'] - valueHint: $.Scalars['String'] -} + export interface ServiceKey { + 'createdAt': $.Scalars['Date'] + 'displayName': $.Scalars['String'] + 'id': $.Scalars['ID'] + 'valueHint': $.Scalars['String'] + } -interface ServiceKeyNode { - displayName: $.Scalars['String'] - id: $.Scalars['String'] - valueHint: $.Scalars['String'] -} + export interface ServiceKeyNode { + 'displayName': $.Scalars['String'] + 'id': $.Scalars['String'] + 'valueHint': $.Scalars['String'] + } -interface ServiceKeyWithValue { - serviceKey: ServiceKey - value: $.Scalars['ID'] -} + export interface ServiceKeyWithValue { + 'serviceKey': Object.ServiceKey + 'value': $.Scalars['ID'] + } -interface SideEffectConfirmation { - ok: $.Scalars['Boolean'] -} + export interface SideEffectConfirmation { + 'ok': $.Scalars['Boolean'] + } -interface StorageValue { - number: $.Scalars['Float'] - unit: StorageUnit | null -} + export interface StorageValue { + 'number': $.Scalars['Float'] + 'unit': Enum.StorageUnit | null + } -interface StripeSubscriptionLineItem { - feature: FeatureHandle - id: $.Scalars['ID'] -} + export interface StripeSubscriptionLineItem { + 'feature': Enum.FeatureHandle + 'id': $.Scalars['ID'] + } -interface System { - accelerate: SystemAccelerate - plans: Array - pulse: SystemPulse -} + export interface System { + 'accelerate': Object.SystemAccelerate + 'plans': Array + 'pulse': Object.SystemPulse + } -interface SystemAccelerate { - defaultRegion: SystemAccelerateRegion - regions: Array -} + export interface SystemAccelerate { + 'defaultRegion': Object.SystemAccelerateRegion + 'regions': Array + } -interface SystemAccelerateRegion { - displayName: $.Scalars['String'] - id: $.Scalars['ID'] -} + export interface SystemAccelerateRegion { + 'displayName': $.Scalars['String'] + 'id': $.Scalars['ID'] + } -interface SystemPulse { - defaultRegion: SystemAccelerateRegion - regions: Array -} + export interface SystemPulse { + 'defaultRegion': Object.SystemAccelerateRegion + 'regions': Array + } -interface TimeInterval { - from: $.Scalars['Date'] - to: $.Scalars['Date'] -} + export interface TimeInterval { + 'from': $.Scalars['Date'] + 'to': $.Scalars['Date'] + } -interface UsageProductAccelerate { - egress: UsageProductAccelerateFeatureEgress - request: UsageProductAccelerateFeatureRequest -} + export interface UsageProductAccelerate { + 'egress': Object.UsageProductAccelerateFeatureEgress + 'request': Object.UsageProductAccelerateFeatureRequest + } -interface UsageProductAccelerateFeatureEgress { - averageResponseSize: $.Scalars['Float'] - total: $.Scalars['Float'] -} + export interface UsageProductAccelerateFeatureEgress { + 'averageResponseSize': $.Scalars['Float'] + 'total': $.Scalars['Float'] + } -interface UsageProductAccelerateFeatureRequest { - all: UsageProductAccelerateFeatureRequestFilterAll - cacheHit: UsageProductAccelerateFeatureRequestFilterCacheHit -} + export interface UsageProductAccelerateFeatureRequest { + 'all': Object.UsageProductAccelerateFeatureRequestFilterAll + 'cacheHit': Object.UsageProductAccelerateFeatureRequestFilterCacheHit + } -interface UsageProductAccelerateFeatureRequestFilterAll { - count: $.Scalars['Int'] -} + export interface UsageProductAccelerateFeatureRequestFilterAll { + 'count': $.Scalars['Int'] + } -interface UsageProductAccelerateFeatureRequestFilterCacheHit { - ratioToMiss: $.Scalars['Int'] -} + export interface UsageProductAccelerateFeatureRequestFilterCacheHit { + 'ratioToMiss': $.Scalars['Int'] + } -interface User { - displayName: $.Scalars['String'] | null - email: $.Scalars['String'] - featureFlags: UserFeatureFlags - handle: $.Scalars['String'] | null - id: $.Scalars['ID'] - image: $.Scalars['String'] | null - preferences: UserPreferences -} + export interface User { + 'displayName': $.Scalars['String'] | null + 'email': $.Scalars['String'] + 'featureFlags': Object.UserFeatureFlags + 'handle': $.Scalars['String'] | null + 'id': $.Scalars['ID'] + 'image': $.Scalars['String'] | null + 'preferences': Object.UserPreferences + } -interface UserFeatureFlags { - adminDashboard: $.Scalars['Boolean'] - mars: $.Scalars['Boolean'] - mercury: $.Scalars['Boolean'] - venus: $.Scalars['Boolean'] -} + export interface UserFeatureFlags { + 'adminDashboard': $.Scalars['Boolean'] + 'mars': $.Scalars['Boolean'] + 'mercury': $.Scalars['Boolean'] + 'venus': $.Scalars['Boolean'] + } -interface UserPreferences { - defaultWorkspace: Workspace | null -} + export interface UserPreferences { + 'defaultWorkspace': Object.Workspace | null + } -interface Workspace { - billingAddress: PhysicalAddress | null - billingEmail: $.Scalars['String'] - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - id: $.Scalars['ID'] - isUsersLastMembership: $.Scalars['Boolean'] - memberships: Array - paymentMethods: Array - planSubscription: PlanSubscription - projects: Array - stripeCustomerId: $.Scalars['String'] - usage: WorkspaceUsage -} + export interface Workspace { + 'billingAddress': Object.PhysicalAddress | null + 'billingEmail': $.Scalars['String'] + 'createdAt': $.Scalars['Date'] + 'displayName': $.Scalars['String'] + 'id': $.Scalars['ID'] + 'isUsersLastMembership': $.Scalars['Boolean'] + 'memberships': Array + 'paymentMethods': Array + 'planSubscription': Object.PlanSubscription + 'projects': Array + 'stripeCustomerId': $.Scalars['String'] + 'usage': Object.WorkspaceUsage + } -interface WorkspaceMembership { - id: $.Scalars['ID'] - role: WorkspaceRole - user: User -} + export interface WorkspaceMembership { + 'id': $.Scalars['ID'] + 'role': Enum.WorkspaceRole + 'user': Object.User + } -interface WorkspaceMembershipNode { - id: $.Scalars['String'] - workspaceId: $.Scalars['ID'] -} + export interface WorkspaceMembershipNode { + 'id': $.Scalars['String'] + 'workspaceId': $.Scalars['ID'] + } -interface WorkspaceNode { - billingEmail: $.Scalars['String'] - displayName: $.Scalars['String'] - id: $.Scalars['String'] -} + export interface WorkspaceNode { + 'billingEmail': $.Scalars['String'] + 'displayName': $.Scalars['String'] + 'id': $.Scalars['String'] + } -interface WorkspaceUsage { - accelerate: UsageProductAccelerate - timeInterval: TimeInterval + export interface WorkspaceUsage { + 'accelerate': Object.UsageProductAccelerate + 'timeInterval': Object.TimeInterval + } } // ------------------------------------------------------------ // // Union // // ------------------------------------------------------------ // -type AccelerateStatus = AccelerateStatusDisabled | AccelerateStatusEnabled - -type FeatureValueType = - | FeatureValueTypeBoolean - | FeatureValueTypeEnum - | FeatureValueTypeNumber - | FeatureValueTypeString - -type Limit = LimitEnum | LimitNumber - -type MutationAccelerateCachePurgeResult = ErrorInternal | SideEffectConfirmation - -type MutationAccelerateDisableResult = ErrorInternal | SideEffectConfirmation - -type MutationAccelerateEnableResult = ErrorInternal | SideEffectConfirmation - -type MutationDatabaseLinkCreateResult = DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound - -type MutationDatabaseLinkDeleteResult = DatabaseLinkNode | ErrorInternal | ErrorUserBusinessResourceNotFound - -type MutationDatabaseLinkUpdateResult = DatabaseLink | ErrorInternal | ErrorUserBusinessResourceNotFound - -type MutationEnvironmentCreateResult = - | Environment - | ErrorInternal - | ErrorUserBusinessPlanLimitHit - | ErrorUserBusinessResourceNotFound - -type MutationEnvironmentDeleteResult = Environment | ErrorInternal | ErrorUserBusinessResourceNotFound - -type MutationEnvironmentUpdateResult = Environment | ErrorInternal | ErrorUserBusinessResourceNotFound - -type MutationProjectCreateResult = - | ErrorInternal - | ErrorUserBusinessPlanLimitHit - | ErrorUserBusinessResourceNotFound - | Project - -type MutationProjectDeleteResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ProjectNode - -type MutationProjectUpdateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | Project - -type MutationPulseDisableResult = ErrorInternal | ErrorUserBusinessResourceNotFound | SideEffectConfirmation - -type MutationPulseEnableResult = ErrorInternal | ErrorUser | SideEffectConfirmation - -type MutationServiceKeyCreateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyWithValue - -type MutationServiceKeyDeleteResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ServiceKeyNode - -type MutationUserUpdateDefaultWorkspaceResult = ErrorInternal | ErrorUserBusinessResourceNotFound | User - -type MutationUserUpdateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | User - -type MutationWorkspaceCreateResult = ErrorInternal | Workspace - -type MutationWorkspaceDeleteResult = - | ErrorInternal - | ErrorUserBusinessDeleteWorkspaceOnPaidPlan - | ErrorUserBusinessResourceNotFound - | WorkspaceNode - -type MutationWorkspaceMembershipCreateResult = - | ErrorInternal - | ErrorUserBusinessResourceNotFound - | ErrorUserBusinessUserAlreadyMemberOfOrganization - | WorkspaceMembership - -type MutationWorkspaceMembershipDeleteResult = - | ErrorInternal - | ErrorUserBusinessResourceNotFound - | WorkspaceMembershipNode - -type MutationWorkspacePlanSubscriptionChangeResult = - | ErrorInternal - | ErrorUserBusinessResourceNotFound - | PlanSubscription - -type MutationWorkspaceUpdateBillingAddressResult = - | ErrorInternal - | ErrorUserBusinessResourceNotFound - | Workspace - -type MutationWorkspaceUpdateBillingEmailResult = ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace - -type MutationWorkspaceUpdateResult = ErrorInternal | ErrorUserBusinessResourceNotFound | Workspace - -type OfferContext = Plan | PlanSubscription - -type OfferTimeInterval = OfferTimeIntervalCycle | OfferTimeIntervalPrevious - -type Price = PriceConstant | PriceTiered - -type PulseStatus = PulseStatusDisabled | PulseStatusEnabled - -type QueryEnvironmentResult = - | Environment - | ErrorInternal - | ErrorUserBusinessNotAuthorized - | ErrorUserBusinessResourceNotFound - -type QueryPlanResult = ErrorInternal | ErrorUserBusinessResourceNotFound | ErrorUserInput | Plan - -type QueryProjectResult = - | ErrorInternal - | ErrorUserBusinessNotAuthorized - | ErrorUserBusinessResourceNotFound - | Project - -type QueryWorkspaceResult = - | ErrorInternal - | ErrorUserBusinessNotAuthorized - | ErrorUserBusinessResourceNotFound - | Workspace +export namespace Union { + export type AccelerateStatus = + | Object.AccelerateStatusDisabled + | Object.AccelerateStatusEnabled + + export type FeatureValueType = + | Object.FeatureValueTypeBoolean + | Object.FeatureValueTypeEnum + | Object.FeatureValueTypeNumber + | Object.FeatureValueTypeString + + export type Limit = + | Object.LimitEnum + | Object.LimitNumber + + export type MutationAccelerateCachePurgeResult = + | Object.ErrorInternal + | Object.SideEffectConfirmation + + export type MutationAccelerateDisableResult = + | Object.ErrorInternal + | Object.SideEffectConfirmation + + export type MutationAccelerateEnableResult = + | Object.ErrorInternal + | Object.SideEffectConfirmation + + export type MutationDatabaseLinkCreateResult = + | Object.DatabaseLink + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + + export type MutationDatabaseLinkDeleteResult = + | Object.DatabaseLinkNode + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + + export type MutationDatabaseLinkUpdateResult = + | Object.DatabaseLink + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + + export type MutationEnvironmentCreateResult = + | Object.Environment + | Object.ErrorInternal + | Object.ErrorUserBusinessPlanLimitHit + | Object.ErrorUserBusinessResourceNotFound + + export type MutationEnvironmentDeleteResult = + | Object.Environment + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + + export type MutationEnvironmentUpdateResult = + | Object.Environment + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + + export type MutationProjectCreateResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessPlanLimitHit + | Object.ErrorUserBusinessResourceNotFound + | Object.Project + + export type MutationProjectDeleteResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ProjectNode + + export type MutationProjectUpdateResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.Project + + export type MutationPulseDisableResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.SideEffectConfirmation + + export type MutationPulseEnableResult = + | Object.ErrorInternal + | Object.ErrorUser + | Object.SideEffectConfirmation + + export type MutationServiceKeyCreateResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ServiceKeyWithValue + + export type MutationServiceKeyDeleteResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ServiceKeyNode + + export type MutationUserUpdateDefaultWorkspaceResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.User + + export type MutationUserUpdateResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.User + + export type MutationWorkspaceCreateResult = + | Object.ErrorInternal + | Object.Workspace + + export type MutationWorkspaceDeleteResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessDeleteWorkspaceOnPaidPlan + | Object.ErrorUserBusinessResourceNotFound + | Object.WorkspaceNode + + export type MutationWorkspaceMembershipCreateResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ErrorUserBusinessUserAlreadyMemberOfOrganization + | Object.WorkspaceMembership + + export type MutationWorkspaceMembershipDeleteResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.WorkspaceMembershipNode + + export type MutationWorkspacePlanSubscriptionChangeResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.PlanSubscription + + export type MutationWorkspaceUpdateBillingAddressResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.Workspace + + export type MutationWorkspaceUpdateBillingEmailResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.Workspace + + export type MutationWorkspaceUpdateResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.Workspace + + export type OfferContext = + | Object.Plan + | Object.PlanSubscription + + export type OfferTimeInterval = + | Object.OfferTimeIntervalCycle + | Object.OfferTimeIntervalPrevious + + export type Price = + | Object.PriceConstant + | Object.PriceTiered + + export type PulseStatus = + | Object.PulseStatusDisabled + | Object.PulseStatusEnabled + + export type QueryEnvironmentResult = + | Object.Environment + | Object.ErrorInternal + | Object.ErrorUserBusinessNotAuthorized + | Object.ErrorUserBusinessResourceNotFound + + export type QueryPlanResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessResourceNotFound + | Object.ErrorUserInput + | Object.Plan + + export type QueryProjectResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessNotAuthorized + | Object.ErrorUserBusinessResourceNotFound + | Object.Project + + export type QueryWorkspaceResult = + | Object.ErrorInternal + | Object.ErrorUserBusinessNotAuthorized + | Object.ErrorUserBusinessResourceNotFound + | Object.Workspace +} diff --git a/src/lib/Code.ts b/src/lib/Code.ts index 74aa6045f..2a58d0c53 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -1,4 +1,5 @@ export namespace Code { + export const propertyAccess = (object: string, name: string) => `${object}.${name}` export const quote = (str: string) => `"${str}"` export const nullable = (type: string) => `${type} | null` export const union = (name: string, types: string[]) => `type ${name} =\n| ${Code.unionItems(types)}` @@ -7,6 +8,7 @@ export namespace Code { export const fieldType = (name: string, type: string) => `"${name}": ${type}` export const fieldTypes = (fieldTypes: string[]) => fieldTypes.join(`\n`) export const inter = (name: string, fields: string) => `interface ${name} {\n${fields}\n}` + export const export$ = (thing: string) => `export ${thing}` export const commentSectionTitle = (title: string) => { const lineSize = 60 const line = `-`.repeat(lineSize) diff --git a/src/lib/generateTypes.ts b/src/lib/generateTypes.ts index 8929e672a..7209560f0 100644 --- a/src/lib/generateTypes.ts +++ b/src/lib/generateTypes.ts @@ -1,6 +1,3 @@ -import { Code } from './Code.js' -import type { AnyClass, NameToClassNamedType } from './graphql.js' -import { getTypeMapByKind, type NameToClass } from './graphql.js' import { readFileSync } from 'fs' import type { GraphQLField, @@ -9,10 +6,24 @@ import type { GraphQLInterfaceType, GraphQLNamedType, GraphQLObjectType, + GraphQLScalarType, } from 'graphql' import { GraphQLNonNull } from 'graphql' import { buildSchema } from 'graphql' import fs from 'node:fs' +import { Code } from './Code.js' +import type { AnyClass, AnyNamedClassName, NameToClassNamedType } from './graphql.js' +import { getTypeMapByKind, NamedNameToClass, type NameToClass } from './graphql.js' +import { entries } from './prelude.js' + +const namespaceNames = { + GraphQLEnumType: 'Enum', + GraphQLInputObjectType: 'InputObject', + GraphQLInterfaceType: 'Interface', + GraphQLObjectType: 'Object', + GraphQLScalarType: 'Scalar', + GraphQLUnionType: 'Union', +} satisfies Record type AnyGraphQLField = GraphQLField | GraphQLInputField // type AnyGraphQLFieldMap = GraphQLFieldMap @@ -48,7 +59,7 @@ const defineConcreteRenderers = < key, (node: any) => { if (!node) return `` - return renderer(node) //eslint-disable-line + return renderer(node) // eslint-disable-line }, ] }), @@ -57,43 +68,47 @@ const defineConcreteRenderers = < const dispatchToPointerRenderer = (node: AnyClass): string => { // @ts-expect-error lookup - const renderer = pointerRenderers[node.constructor.name] //eslint-disable-line + const renderer = pointerRenderers[node.constructor.name] // eslint-disable-line if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) - return renderer(node) //eslint-disable-line + return renderer(node) // eslint-disable-line } const dispatchToConcreteRenderer = (node: GraphQLNamedType): string => { // @ts-expect-error lookup - const renderer = concreteRenderers[node.constructor.name] //eslint-disable-line + const renderer = concreteRenderers[node.constructor.name] // eslint-disable-line if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) - return renderer(node) //eslint-disable-line + return renderer(node) // eslint-disable-line } const pointerRenderers = definePointerRenderers({ GraphQLNonNull: (node) => dispatchToPointerRenderer(node.ofType), - GraphQLEnumType: (node) => node.name, - GraphQLInputObjectType: (node) => node.name, - GraphQLInterfaceType: (node) => node.name, + GraphQLEnumType: (node) => Code.propertyAccess(namespaceNames.GraphQLEnumType, node.name), + GraphQLInputObjectType: (node) => Code.propertyAccess(namespaceNames.GraphQLInputObjectType, node.name), + GraphQLInterfaceType: (node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), GraphQLList: (node) => Code.list(dispatchToPointerRenderer(node.ofType)), - GraphQLObjectType: (node) => node.name, + GraphQLObjectType: (node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), GraphQLScalarType: (node) => `$.Scalars[${Code.quote(node.name)}]`, GraphQLUnionType: (node) => Code.unionItems(node.getTypes().map((_) => dispatchToPointerRenderer(_))), }) const concreteRenderers = defineConcreteRenderers({ GraphQLEnumType: (node) => - Code.union( - node.name, - node.getValues().map((_) => Code.quote(_.name)), + Code.export$( + Code.union( + node.name, + node.getValues().map((_) => Code.quote(_.name)), + ), ), - GraphQLInputObjectType: (node) => Code.inter(node.name, renderFields(node)), - GraphQLInterfaceType: (node) => Code.inter(node.name, renderFields(node)), - GraphQLObjectType: (node) => Code.inter(node.name, renderFields(node)), + GraphQLInputObjectType: (node) => Code.export$(Code.inter(node.name, renderFields(node))), + GraphQLInterfaceType: (node) => Code.export$(Code.inter(node.name, renderFields(node))), + GraphQLObjectType: (node) => Code.export$(Code.inter(node.name, renderFields(node))), GraphQLScalarType: () => ``, GraphQLUnionType: (node) => - Code.union( - node.name, - node.getTypes().map((_) => dispatchToPointerRenderer(_)), + Code.export$( + Code.union( + node.name, + node.getTypes().map((_) => dispatchToPointerRenderer(_)), + ), ), }) @@ -106,7 +121,7 @@ const renderFields = (node: AnyGraphQLFieldsType): string => { const renderField = (field: AnyGraphQLField): string => { const [fieldType, nullable] = field.type instanceof GraphQLNonNull ? [field.type.ofType, false] : [field.type, true] - return nullable ? Code.nullable(dispatchToPointerRenderer(fieldType)) : dispatchToPointerRenderer(fieldType) //eslint-disable-line + return nullable ? Code.nullable(dispatchToPointerRenderer(fieldType)) : dispatchToPointerRenderer(fieldType) // eslint-disable-line } const scalarTypeMap: Record = { @@ -141,15 +156,18 @@ const generateSchemaTypes = (input: Input) => { } ` - for (const [name, types] of Object.entries(typeMapByKind)) { + for (const [name, types] of entries(typeMapByKind)) { if (name === `GraphQLScalarType`) continue - code += Code.commentSectionTitle(name.replace(/^GraphQL/, ``).replace(/Type$/, ``)) + const namespaceName = name === 'GraphQLRootTypes' ? 'Root' : namespaceNames[name] + code += Code.commentSectionTitle(namespaceName) + code += `export namespace ${namespaceName} {\n` if (types.length === 0) { code += `// -- no types --\n` continue } code += types.map(dispatchToConcreteRenderer).join(`\n\n`) + code += `}` } return code diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index 7ec766750..c0b690e2c 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -68,17 +68,25 @@ export const NameToClassNamedType = { export type NameToClassNamedType = typeof NameToClassNamedType -export const NameToClass = { - GraphQLNonNull: GraphQLNonNull, +export const NamedNameToClass = { GraphQLScalarType: GraphQLScalarType, GraphQLObjectType: GraphQLObjectType, GraphQLInterfaceType: GraphQLInterfaceType, GraphQLUnionType: GraphQLUnionType, GraphQLEnumType: GraphQLEnumType, GraphQLInputObjectType: GraphQLInputObjectType, +} as const + +export type NamedNameToClass = typeof NamedNameToClass + +export const NameToClass = { + GraphQLNonNull: GraphQLNonNull, GraphQLList: GraphQLList, + ...NamedNameToClass, } as const export type NameToClass = typeof NameToClass +export type AnyNamedClassName = keyof NamedNameToClass + export type AnyClass = InstanceType diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index 65f59c9e7..75c351ce1 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -83,3 +83,6 @@ export const casesExhausted = (value: never): never => { export const isPlainObject = (value: unknown): value is object => { return typeof value === `object` && value !== null && !Array.isArray(value) } + +export const entries = >(obj: T) => + Object.entries(obj) as [keyof T, T[keyof T]][] From a628d60acdd5e55b754a029baf7aa35074379cd8 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 17 Feb 2024 18:25:15 -0500 Subject: [PATCH 05/71] quick demo --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index ceb6140b6..4808886e6 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ }, "homepage": "https://github.com/jasonkuhrt/graphql-request", "scripts": { + "demo": "tsx src/lib/generateTypes.ts && dprint fmt src/demo.ts", "dev": "rm -rf dist && tsc --watch", "format": "pnpm build:docs && dprint fmt", "lint": "eslint . --ext .ts,.tsx --fix", From ba0f95ac39470598296b8c01d6f7697cd9e44944 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 17 Feb 2024 18:27:37 -0500 Subject: [PATCH 06/71] no quote --- src/demo.ts | 762 ++++++++++++++++++++++++------------------------ src/lib/Code.ts | 2 +- 2 files changed, 382 insertions(+), 382 deletions(-) diff --git a/src/demo.ts b/src/demo.ts index 721059af9..a104ec9b3 100644 --- a/src/demo.ts +++ b/src/demo.ts @@ -1,11 +1,11 @@ namespace $ { export interface Scalars { - 'Boolean': boolean - 'Int': number - 'String': string - 'ID': string - 'Date': any - 'Float': number + Boolean: boolean + Int: number + String: string + ID: string + Date: any + Float: number } } @@ -15,133 +15,133 @@ namespace $ { export namespace Root { export interface Mutation { - 'accelerateCachePurge': + accelerateCachePurge: | Object.ErrorInternal | Object.SideEffectConfirmation - 'accelerateDisable': + accelerateDisable: | Object.ErrorInternal | Object.SideEffectConfirmation - 'accelerateEnable': + accelerateEnable: | Object.ErrorInternal | Object.SideEffectConfirmation - 'databaseLinkCreate': + databaseLinkCreate: | Object.DatabaseLink | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound - 'databaseLinkDelete': + databaseLinkDelete: | Object.DatabaseLinkNode | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound - 'databaseLinkUpdate': + databaseLinkUpdate: | Object.DatabaseLink | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound - 'environmentCreate': + environmentCreate: | Object.Environment | Object.ErrorInternal | Object.ErrorUserBusinessPlanLimitHit | Object.ErrorUserBusinessResourceNotFound - 'environmentDelete': + environmentDelete: | Object.Environment | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound - 'environmentUpdate': + environmentUpdate: | Object.Environment | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound - 'projectCreate': + projectCreate: | Object.ErrorInternal | Object.ErrorUserBusinessPlanLimitHit | Object.ErrorUserBusinessResourceNotFound | Object.Project - 'projectDelete': + projectDelete: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.ProjectNode - 'projectUpdate': + projectUpdate: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.Project - 'pulseDisable': + pulseDisable: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.SideEffectConfirmation - 'pulseEnable': + pulseEnable: | Object.ErrorInternal | Object.ErrorUser | Object.SideEffectConfirmation - 'serviceKeyCreate': + serviceKeyCreate: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.ServiceKeyWithValue - 'serviceKeyDelete': + serviceKeyDelete: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.ServiceKeyNode - 'userUpdate': + userUpdate: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.User - 'userUpdateDefaultWorkspace': + userUpdateDefaultWorkspace: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.User - 'workspaceCreate': + workspaceCreate: | Object.ErrorInternal | Object.Workspace - 'workspaceDelete': + workspaceDelete: | Object.ErrorInternal | Object.ErrorUserBusinessDeleteWorkspaceOnPaidPlan | Object.ErrorUserBusinessResourceNotFound | Object.WorkspaceNode - 'workspaceMembershipCreate': + workspaceMembershipCreate: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.ErrorUserBusinessUserAlreadyMemberOfOrganization | Object.WorkspaceMembership - 'workspaceMembershipDelete': + workspaceMembershipDelete: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.WorkspaceMembershipNode - 'workspacePlanSubscriptionChange': + workspacePlanSubscriptionChange: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.PlanSubscription - 'workspaceUpdate': + workspaceUpdate: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.Workspace - 'workspaceUpdateBillingAddress': + workspaceUpdateBillingAddress: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.Workspace - 'workspaceUpdateBillingEmail': + workspaceUpdateBillingEmail: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.Workspace } export interface Query { - 'environment': + environment: | Object.Environment | Object.ErrorInternal | Object.ErrorUserBusinessNotAuthorized | Object.ErrorUserBusinessResourceNotFound - 'me': Object.Me - 'plan': + me: Object.Me + plan: | Object.ErrorInternal | Object.ErrorUserBusinessResourceNotFound | Object.ErrorUserInput | Object.Plan - 'project': + project: | Object.ErrorInternal | Object.ErrorUserBusinessNotAuthorized | Object.ErrorUserBusinessResourceNotFound | Object.Project - 'serviceKeys': Array - 'system': Object.System - 'user': Object.User - 'workspace': + serviceKeys: Array + system: Object.System + user: Object.User + workspace: | Object.ErrorInternal | Object.ErrorUserBusinessNotAuthorized | Object.ErrorUserBusinessResourceNotFound @@ -476,145 +476,145 @@ export namespace Enum { export namespace InputObject { export interface MutationAccelerateCachePurgeInput { - 'environmentId': $.Scalars['ID'] + environmentId: $.Scalars['ID'] } export interface MutationAccelerateDisableInput { - 'environmentId': $.Scalars['ID'] + environmentId: $.Scalars['ID'] } export interface MutationAccelerateEnableInput { - 'databaseLinkId': $.Scalars['ID'] + databaseLinkId: $.Scalars['ID'] } export interface MutationDatabaseLinkCreateInput { - 'connectionString': $.Scalars['String'] - 'displayName': $.Scalars['String'] | null - 'environmentId': $.Scalars['ID'] - 'regionId': $.Scalars['String'] | null + connectionString: $.Scalars['String'] + displayName: $.Scalars['String'] | null + environmentId: $.Scalars['ID'] + regionId: $.Scalars['String'] | null } export interface MutationDatabaseLinkDeleteInput { - 'id': $.Scalars['ID'] + id: $.Scalars['ID'] } export interface MutationDatabaseLinkUpdateInput { - 'connectionString': $.Scalars['String'] - 'id': $.Scalars['ID'] - 'regionId': $.Scalars['String'] + connectionString: $.Scalars['String'] + id: $.Scalars['ID'] + regionId: $.Scalars['String'] } export interface MutationEnvironmentCreateInput { - 'displayName': $.Scalars['String'] | null - 'isDefault': $.Scalars['Boolean'] | null - 'projectId': $.Scalars['ID'] + displayName: $.Scalars['String'] | null + isDefault: $.Scalars['Boolean'] | null + projectId: $.Scalars['ID'] } export interface MutationEnvironmentDeleteInput { - 'id': $.Scalars['ID'] + id: $.Scalars['ID'] } export interface MutationEnvironmentUpdateInput { - 'displayName': $.Scalars['String'] | null - 'id': $.Scalars['ID'] - 'isDefault': $.Scalars['Boolean'] | null + displayName: $.Scalars['String'] | null + id: $.Scalars['ID'] + isDefault: $.Scalars['Boolean'] | null } export interface MutationProjectCreateInput { - 'displayName': $.Scalars['String'] | null - 'workspaceId': $.Scalars['ID'] + displayName: $.Scalars['String'] | null + workspaceId: $.Scalars['ID'] } export interface MutationProjectDeleteInput { - 'id': $.Scalars['ID'] + id: $.Scalars['ID'] } export interface MutationProjectUpdateInput { - 'displayName': $.Scalars['String'] | null - 'id': $.Scalars['ID'] + displayName: $.Scalars['String'] | null + id: $.Scalars['ID'] } export interface MutationPulseDisableInput { - 'environmentId': $.Scalars['String'] + environmentId: $.Scalars['String'] } export interface MutationPulseEnableInput { - 'databaseLinkId': $.Scalars['String'] + databaseLinkId: $.Scalars['String'] } export interface MutationServiceKeyCreateInput { - 'displayName': $.Scalars['String'] | null - 'environmentId': $.Scalars['ID'] + displayName: $.Scalars['String'] | null + environmentId: $.Scalars['ID'] } export interface MutationServiceKeyDeleteInput { - 'id': $.Scalars['String'] + id: $.Scalars['String'] } export interface MutationUserUpdateDefaultWorkspaceInput { - 'workspaceId': $.Scalars['ID'] + workspaceId: $.Scalars['ID'] } export interface MutationUserUpdateInput { - 'displayName': $.Scalars['String'] | null - 'id': $.Scalars['ID'] + displayName: $.Scalars['String'] | null + id: $.Scalars['ID'] } export interface MutationWorkspaceCreateInput { - 'displayName': $.Scalars['String'] | null + displayName: $.Scalars['String'] | null } export interface MutationWorkspaceDeleteInput { - 'id': $.Scalars['ID'] + id: $.Scalars['ID'] } export interface MutationWorkspaceMembershipCreateInput { - 'email': $.Scalars['String'] - 'role': Enum.WorkspaceRole - 'workspaceId': $.Scalars['ID'] + email: $.Scalars['String'] + role: Enum.WorkspaceRole + workspaceId: $.Scalars['ID'] } export interface MutationWorkspaceMembershipDeleteInput { - 'id': $.Scalars['ID'] + id: $.Scalars['ID'] } export interface MutationWorkspacePlanSubscriptionChangeInput { - 'targetPlanId': $.Scalars['ID'] - 'workspaceId': $.Scalars['ID'] + targetPlanId: $.Scalars['ID'] + workspaceId: $.Scalars['ID'] } export interface MutationWorkspaceUpdateBillingAddressInput { - 'address': InputObject.PhysicalAddressInput - 'id': $.Scalars['ID'] + address: InputObject.PhysicalAddressInput + id: $.Scalars['ID'] } export interface MutationWorkspaceUpdateBillingEmailInput { - 'email': $.Scalars['String'] - 'id': $.Scalars['ID'] + email: $.Scalars['String'] + id: $.Scalars['ID'] } export interface MutationWorkspaceUpdateInput { - 'displayName': $.Scalars['String'] | null - 'id': $.Scalars['ID'] + displayName: $.Scalars['String'] | null + id: $.Scalars['ID'] } export interface PhysicalAddressInput { - 'addressLine1': $.Scalars['String'] | null - 'addressLine2': $.Scalars['String'] | null - 'city': $.Scalars['String'] | null - 'country': Enum.CountryCode | null - 'postalCodeOrZIP': $.Scalars['String'] | null - 'region': $.Scalars['String'] | null + addressLine1: $.Scalars['String'] | null + addressLine2: $.Scalars['String'] | null + city: $.Scalars['String'] | null + country: Enum.CountryCode | null + postalCodeOrZIP: $.Scalars['String'] | null + region: $.Scalars['String'] | null } export interface TimeIntervalInput { - 'fromDate': $.Scalars['Date'] | null - 'fromDateHandle': Enum.PreviousDateHandle | null - 'toDate': $.Scalars['Date'] | null + fromDate: $.Scalars['Date'] | null + fromDateHandle: Enum.PreviousDateHandle | null + toDate: $.Scalars['Date'] | null } export interface WorkspaceOrderBy { - 'displayName': Enum.Order | null + displayName: Enum.Order | null } } @@ -624,38 +624,38 @@ export namespace InputObject { export namespace Interface { export interface Error { - 'message': $.Scalars['String'] + message: $.Scalars['String'] } export interface Feature { - 'displayName': $.Scalars['String'] | null - 'handle': Enum.FeatureHandle - 'id': $.Scalars['ID'] - 'stripeProductId': $.Scalars['String'] + displayName: $.Scalars['String'] | null + handle: Enum.FeatureHandle + id: $.Scalars['ID'] + stripeProductId: $.Scalars['String'] } export interface Node { - 'id': $.Scalars['String'] + id: $.Scalars['String'] } export interface Offer { - 'context': + context: | Object.Plan | Object.PlanSubscription - 'id': $.Scalars['ID'] - 'price': + id: $.Scalars['ID'] + price: | Object.PriceConstant | Object.PriceTiered | null } export interface PriceI { - 'id': $.Scalars['String'] - 'stripePriceId': $.Scalars['ID'] + id: $.Scalars['String'] + stripePriceId: $.Scalars['ID'] } export interface ProductStatus { - 'enabled': $.Scalars['Boolean'] + enabled: $.Scalars['Boolean'] } } @@ -665,210 +665,210 @@ export namespace Interface { export namespace Object { export interface AccelerateStatusDisabled { - 'enabled': $.Scalars['Boolean'] + enabled: $.Scalars['Boolean'] } export interface AccelerateStatusEnabled { - 'enabled': $.Scalars['Boolean'] + enabled: $.Scalars['Boolean'] } export interface Count { - 'number': $.Scalars['Int'] + number: $.Scalars['Int'] } export interface DatabaseLink { - 'connectionStringHint': $.Scalars['String'] - 'id': $.Scalars['ID'] - 'protocol': $.Scalars['String'] - 'region': $.Scalars['String'] | null + connectionStringHint: $.Scalars['String'] + id: $.Scalars['ID'] + protocol: $.Scalars['String'] + region: $.Scalars['String'] | null } export interface DatabaseLinkNode { - 'connectionStringHint': $.Scalars['ID'] - 'displayName': $.Scalars['String'] - 'id': $.Scalars['String'] + connectionStringHint: $.Scalars['ID'] + displayName: $.Scalars['String'] + id: $.Scalars['String'] } export interface Environment { - 'accelerate': Object.EnvironmentAccelerate - 'createdAt': $.Scalars['Date'] - 'displayName': $.Scalars['String'] - 'id': $.Scalars['ID'] - 'isDefault': $.Scalars['Boolean'] - 'project': Object.Project - 'pulse': Object.EnvironmentPulse - 'serviceKeys': Array - 'tenantId': $.Scalars['ID'] + accelerate: Object.EnvironmentAccelerate + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + id: $.Scalars['ID'] + isDefault: $.Scalars['Boolean'] + project: Object.Project + pulse: Object.EnvironmentPulse + serviceKeys: Array + tenantId: $.Scalars['ID'] } export interface EnvironmentAccelerate { - 'databaseLink': Object.DatabaseLink | null - 'holds': Array - 'status': + databaseLink: Object.DatabaseLink | null + holds: Array + status: | Object.AccelerateStatusDisabled | Object.AccelerateStatusEnabled - 'usage': Object.EnvironmentAccelerateUsage + usage: Object.EnvironmentAccelerateUsage } export interface EnvironmentAccelerateTimeSeriesPoints { - 'queries': Object.EnvironmentAccelerateUsageTimeSeriesPointsQueries - 'timestamps': Array<$.Scalars['Date']> + queries: Object.EnvironmentAccelerateUsageTimeSeriesPointsQueries + timestamps: Array<$.Scalars['Date']> } export interface EnvironmentAccelerateUsage { - 'latency': Object.EnvironmentAccelerateUsageLatency - 'overview': Object.EnvironmentAccelerateUsageOverview - 'timeInterval': Object.TimeInterval - 'timeSeries': Object.EnvironmentAccelerateUsageTimeSeries + latency: Object.EnvironmentAccelerateUsageLatency + overview: Object.EnvironmentAccelerateUsageOverview + timeInterval: Object.TimeInterval + timeSeries: Object.EnvironmentAccelerateUsageTimeSeries } export interface EnvironmentAccelerateUsageLatency { - 'queries': Object.EnvironmentAccelerateUsageLatencyQueries + queries: Object.EnvironmentAccelerateUsageLatencyQueries } export interface EnvironmentAccelerateUsageLatencyQueries { - 'cached': Object.EnvironmentAccelerateUsageLatencyQuery - 'origin': Object.EnvironmentAccelerateUsageLatencyQuery + cached: Object.EnvironmentAccelerateUsageLatencyQuery + origin: Object.EnvironmentAccelerateUsageLatencyQuery } export interface EnvironmentAccelerateUsageLatencyQuery { - 'count': Object.Count - 'durationAverage': Object.MetricValue - 'durationPercentiles': Array + count: Object.Count + durationAverage: Object.MetricValue + durationPercentiles: Array } export interface EnvironmentAccelerateUsageOverview { - 'egress': Object.EnvironmentAccelerateUsageOverviewEgress - 'queries': Object.EnvironmentAccelerateUsageOverviewQueries + egress: Object.EnvironmentAccelerateUsageOverviewEgress + queries: Object.EnvironmentAccelerateUsageOverviewQueries } export interface EnvironmentAccelerateUsageOverviewCacheHit { - 'ratioToMiss': Object.MetricValue + ratioToMiss: Object.MetricValue } export interface EnvironmentAccelerateUsageOverviewEgress { - 'averageResponseSize': Object.StorageValue - 'requestsServedFromOrigin': Object.Count - 'total': Object.StorageValue + averageResponseSize: Object.StorageValue + requestsServedFromOrigin: Object.Count + total: Object.StorageValue } export interface EnvironmentAccelerateUsageOverviewQueries { - 'cacheHit': Object.EnvironmentAccelerateUsageOverviewCacheHit - 'cacheableCount': Object.Count - 'totalCount': Object.Count + cacheHit: Object.EnvironmentAccelerateUsageOverviewCacheHit + cacheableCount: Object.Count + totalCount: Object.Count } export interface EnvironmentAccelerateUsageTimeSeries { - 'points': Object.EnvironmentAccelerateTimeSeriesPoints + points: Object.EnvironmentAccelerateTimeSeriesPoints } export interface EnvironmentAccelerateUsageTimeSeriesPointsQueries { - 'miss': Array | null - 'none': Array | null - 'swr': Array | null - 'ttl': Array | null + miss: Array | null + none: Array | null + swr: Array | null + ttl: Array | null } export interface EnvironmentAccelerateUsageTimeSeriesPointsQuery { - 'count': Object.Count - 'timestamp': $.Scalars['Date'] + count: Object.Count + timestamp: $.Scalars['Date'] } export interface EnvironmentPulse { - 'databaseLink': Object.DatabaseLink | null - 'status': + databaseLink: Object.DatabaseLink | null + status: | Object.PulseStatusDisabled | Object.PulseStatusEnabled } export interface ErrorInternal { - 'message': $.Scalars['String'] + message: $.Scalars['String'] } export interface ErrorUser { - 'message': $.Scalars['String'] + message: $.Scalars['String'] } export interface ErrorUserBusinessDeleteWorkspaceOnPaidPlan { - 'context': Object.ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext - 'message': $.Scalars['String'] + context: Object.ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext + message: $.Scalars['String'] } export interface ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext { - 'plan': Object.Plan + plan: Object.Plan } export interface ErrorUserBusinessNotAuthorized { - 'message': $.Scalars['String'] + message: $.Scalars['String'] } export interface ErrorUserBusinessPlanLimitHit { - 'context': Object.ErrorUserBusinessPlanLimitHitContext - 'message': $.Scalars['String'] + context: Object.ErrorUserBusinessPlanLimitHitContext + message: $.Scalars['String'] } export interface ErrorUserBusinessPlanLimitHitContext { - 'featureHandle': $.Scalars['String'] | null + featureHandle: $.Scalars['String'] | null } export interface ErrorUserBusinessResourceNotFound { - 'context': Object.ErrorUserBusinessResourceNotFoundContext - 'message': $.Scalars['String'] + context: Object.ErrorUserBusinessResourceNotFoundContext + message: $.Scalars['String'] } export interface ErrorUserBusinessResourceNotFoundContext { - 'id': $.Scalars['ID'] | null - 'typeName': Enum.ResourceType + id: $.Scalars['ID'] | null + typeName: Enum.ResourceType } export interface ErrorUserBusinessUserAlreadyMemberOfOrganization { - 'context': Object.ErrorUserBusinessUserAlreadyMemberOfOrganizationContext - 'message': $.Scalars['String'] + context: Object.ErrorUserBusinessUserAlreadyMemberOfOrganizationContext + message: $.Scalars['String'] } export interface ErrorUserBusinessUserAlreadyMemberOfOrganizationContext { - 'user': Object.User - 'workspace': Object.Workspace + user: Object.User + workspace: Object.Workspace } export interface ErrorUserInput { - 'message': $.Scalars['String'] + message: $.Scalars['String'] } export interface FeatureAbstract { - 'displayName': $.Scalars['String'] | null - 'handle': Enum.FeatureHandle - 'id': $.Scalars['ID'] - 'stripeProductId': $.Scalars['String'] + displayName: $.Scalars['String'] | null + handle: Enum.FeatureHandle + id: $.Scalars['ID'] + stripeProductId: $.Scalars['String'] } export interface FeatureResourceAggregation { - 'displayName': $.Scalars['String'] | null - 'handle': Enum.FeatureHandle - 'id': $.Scalars['ID'] - 'resource': $.Scalars['String'] - 'scope': $.Scalars['String'] - 'stripeProductId': $.Scalars['String'] - 'valueResolver': Object.FeatureResourceAggregationValueResolver + displayName: $.Scalars['String'] | null + handle: Enum.FeatureHandle + id: $.Scalars['ID'] + resource: $.Scalars['String'] + scope: $.Scalars['String'] + stripeProductId: $.Scalars['String'] + valueResolver: Object.FeatureResourceAggregationValueResolver } export interface FeatureResourceAggregationValueResolver { - 'type': Enum.FeatureResourceAggregationValueResolverType + type: Enum.FeatureResourceAggregationValueResolverType } export interface FeatureResourceProperty { - 'displayName': $.Scalars['String'] | null - 'handle': Enum.FeatureHandle - 'id': $.Scalars['ID'] - 'resource': $.Scalars['String'] - 'scope': $.Scalars['String'] - 'stripeProductId': $.Scalars['String'] - 'valueResolver': Object.FeatureResourcePropertyValueResolver + displayName: $.Scalars['String'] | null + handle: Enum.FeatureHandle + id: $.Scalars['ID'] + resource: $.Scalars['String'] + scope: $.Scalars['String'] + stripeProductId: $.Scalars['String'] + valueResolver: Object.FeatureResourcePropertyValueResolver } export interface FeatureResourcePropertyValueResolver { - 'field': $.Scalars['String'] - 'type': + field: $.Scalars['String'] + type: | Object.FeatureValueTypeBoolean | Object.FeatureValueTypeEnum | Object.FeatureValueTypeNumber @@ -876,11 +876,11 @@ export namespace Object { } export interface FeatureValue { - 'displayName': $.Scalars['String'] | null - 'handle': Enum.FeatureHandle - 'id': $.Scalars['ID'] - 'stripeProductId': $.Scalars['String'] - 'valueType': + displayName: $.Scalars['String'] | null + handle: Enum.FeatureHandle + id: $.Scalars['ID'] + stripeProductId: $.Scalars['String'] + valueType: | Object.FeatureValueTypeBoolean | Object.FeatureValueTypeEnum | Object.FeatureValueTypeNumber @@ -888,389 +888,389 @@ export namespace Object { } export interface FeatureValueTypeBoolean { - 'displayName': $.Scalars['String'] + displayName: $.Scalars['String'] } export interface FeatureValueTypeEnum { - 'displayName': $.Scalars['String'] - 'members': Array + displayName: $.Scalars['String'] + members: Array } export interface FeatureValueTypeEnumMember { - 'description': $.Scalars['String'] | null - 'value': $.Scalars['String'] + description: $.Scalars['String'] | null + value: $.Scalars['String'] } export interface FeatureValueTypeNumber { - 'displayName': $.Scalars['String'] + displayName: $.Scalars['String'] } export interface FeatureValueTypeString { - 'displayName': $.Scalars['String'] + displayName: $.Scalars['String'] } export interface LimitEnum { - 'allowed': Array<$.Scalars['String']> + allowed: Array<$.Scalars['String']> } export interface LimitNumber { - 'amount': $.Scalars['Int'] - 'type': Enum.NumberPredicateFnType + amount: $.Scalars['Int'] + type: Enum.NumberPredicateFnType } export interface Me { - 'user': Object.User - 'workspaces': Array + user: Object.User + workspaces: Array } export interface MetricValue { - 'number': $.Scalars['Float'] - 'unit': Enum.MetricUnit | null + number: $.Scalars['Float'] + unit: Enum.MetricUnit | null } export interface OfferAbstract { - 'context': + context: | Object.Plan | Object.PlanSubscription - 'feature': Object.FeatureAbstract - 'id': $.Scalars['ID'] - 'price': + feature: Object.FeatureAbstract + id: $.Scalars['ID'] + price: | Object.PriceConstant | Object.PriceTiered | null } export interface OfferResourceAggregation { - 'context': + context: | Object.Plan | Object.PlanSubscription - 'feature': Object.FeatureResourceAggregation - 'id': $.Scalars['ID'] - 'limit': + feature: Object.FeatureResourceAggregation + id: $.Scalars['ID'] + limit: | Object.LimitEnum | Object.LimitNumber | null - 'price': + price: | Object.PriceConstant | Object.PriceTiered | null - 'timeInterval': + timeInterval: | Object.OfferTimeIntervalCycle | Object.OfferTimeIntervalPrevious | null } export interface OfferResourceProperty { - 'context': + context: | Object.Plan | Object.PlanSubscription - 'feature': Object.FeatureResourceProperty - 'id': $.Scalars['ID'] - 'limit': + feature: Object.FeatureResourceProperty + id: $.Scalars['ID'] + limit: | Object.LimitEnum | Object.LimitNumber | null - 'price': + price: | Object.PriceConstant | Object.PriceTiered | null - 'timeInterval': + timeInterval: | Object.OfferTimeIntervalCycle | Object.OfferTimeIntervalPrevious | null } export interface OfferTimeIntervalCycle { - 'ok': $.Scalars['Boolean'] + ok: $.Scalars['Boolean'] } export interface OfferTimeIntervalPrevious { - 'milliseconds': $.Scalars['Int'] + milliseconds: $.Scalars['Int'] } export interface OfferValue { - 'context': + context: | Object.Plan | Object.PlanSubscription - 'feature': Object.FeatureValue - 'id': $.Scalars['ID'] - 'limit': + feature: Object.FeatureValue + id: $.Scalars['ID'] + limit: | Object.LimitEnum | Object.LimitNumber | null - 'price': + price: | Object.PriceConstant | Object.PriceTiered | null - 'value': $.Scalars['String'] + value: $.Scalars['String'] } export interface PaymentMethod { - 'card': Object.PaymentMethodCard - 'id': $.Scalars['ID'] - 'isDefault': $.Scalars['Boolean'] + card: Object.PaymentMethodCard + id: $.Scalars['ID'] + isDefault: $.Scalars['Boolean'] } export interface PaymentMethodCard { - 'brand': Enum.PaymentMethodCardBrand - 'expiryMonth': $.Scalars['Int'] - 'expiryYear': $.Scalars['Int'] - 'id': $.Scalars['ID'] - 'last4': $.Scalars['String'] + brand: Enum.PaymentMethodCardBrand + expiryMonth: $.Scalars['Int'] + expiryYear: $.Scalars['Int'] + id: $.Scalars['ID'] + last4: $.Scalars['String'] } export interface Percentile { - 'percentile': $.Scalars['Int'] - 'value': Object.MetricValue + percentile: $.Scalars['Int'] + value: Object.MetricValue } export interface PhysicalAddress { - 'addressLine1': $.Scalars['String'] | null - 'addressLine2': $.Scalars['String'] | null - 'city': $.Scalars['String'] | null - 'country': $.Scalars['String'] | null - 'postalCodeOrZIP': $.Scalars['String'] | null - 'region': $.Scalars['String'] | null + addressLine1: $.Scalars['String'] | null + addressLine2: $.Scalars['String'] | null + city: $.Scalars['String'] | null + country: $.Scalars['String'] | null + postalCodeOrZIP: $.Scalars['String'] | null + region: $.Scalars['String'] | null } export interface Plan { - 'displayName': $.Scalars['String'] - 'handle': $.Scalars['String'] - 'id': $.Scalars['ID'] - 'isDefault': $.Scalars['Boolean'] - 'isFree': $.Scalars['Boolean'] - 'offers': Object.PlanOffers - 'power': $.Scalars['Int'] - 'selectable': $.Scalars['Boolean'] - 'version': $.Scalars['Int'] - 'versionIsLatest': $.Scalars['Boolean'] - 'versions': Object.PlanVersions + displayName: $.Scalars['String'] + handle: $.Scalars['String'] + id: $.Scalars['ID'] + isDefault: $.Scalars['Boolean'] + isFree: $.Scalars['Boolean'] + offers: Object.PlanOffers + power: $.Scalars['Int'] + selectable: $.Scalars['Boolean'] + version: $.Scalars['Int'] + versionIsLatest: $.Scalars['Boolean'] + versions: Object.PlanVersions } export interface PlanOffers { - 'accelerate': Object.PlanOffersAccelerate - 'conductor': Object.PlanOffersConductor - 'platform': Object.PlanOffersPlatform + accelerate: Object.PlanOffersAccelerate + conductor: Object.PlanOffersConductor + platform: Object.PlanOffersPlatform } export interface PlanOffersAccelerate { - 'egress': Object.OfferResourceProperty - 'purgeCache': Object.OfferResourceAggregation - 'query': Object.OfferResourceAggregation + egress: Object.OfferResourceProperty + purgeCache: Object.OfferResourceAggregation + query: Object.OfferResourceAggregation } export interface PlanOffersConductor { - 'createProject': Object.OfferResourceAggregation - 'organizationRole': Object.OfferResourceProperty + createProject: Object.OfferResourceAggregation + organizationRole: Object.OfferResourceProperty } export interface PlanOffersPlatform { - 'access': Object.OfferAbstract - 'support': Object.OfferValue + access: Object.OfferAbstract + support: Object.OfferValue } export interface PlanSubscription { - 'createdAt': $.Scalars['Date'] - 'id': $.Scalars['ID'] - 'plan': Object.Plan - 'stripeSubscriptionId': $.Scalars['String'] | null - 'stripeSubscriptionLineItems': Array - 'workspace': Object.Workspace + createdAt: $.Scalars['Date'] + id: $.Scalars['ID'] + plan: Object.Plan + stripeSubscriptionId: $.Scalars['String'] | null + stripeSubscriptionLineItems: Array + workspace: Object.Workspace } export interface PlanVersions { - 'isLatest': $.Scalars['Boolean'] - 'next': Array - 'previous': Array + isLatest: $.Scalars['Boolean'] + next: Array + previous: Array } export interface PriceConstant { - 'cents': $.Scalars['Int'] - 'id': $.Scalars['String'] - 'stripePriceId': $.Scalars['ID'] + cents: $.Scalars['Int'] + id: $.Scalars['String'] + stripePriceId: $.Scalars['ID'] } export interface PriceTiered { - 'id': $.Scalars['String'] - 'stripePriceId': $.Scalars['ID'] - 'tiers': Array + id: $.Scalars['String'] + stripePriceId: $.Scalars['ID'] + tiers: Array } export interface PriceTieredTier { - 'cents': $.Scalars['Float'] - 'from': $.Scalars['Int'] - 'to': $.Scalars['Int'] | null + cents: $.Scalars['Float'] + from: $.Scalars['Int'] + to: $.Scalars['Int'] | null } export interface ProductHold { - 'createdAt': $.Scalars['Int'] - 'expiresAt': $.Scalars['Int'] - 'reason': $.Scalars['String'] + createdAt: $.Scalars['Int'] + expiresAt: $.Scalars['Int'] + reason: $.Scalars['String'] } export interface Project { - 'accelerate': Object.EnvironmentAccelerate - 'createdAt': $.Scalars['Date'] - 'displayName': $.Scalars['String'] - 'environments': Array - 'id': $.Scalars['ID'] - 'pulse': Object.EnvironmentPulse - 'workspace': Object.Workspace + accelerate: Object.EnvironmentAccelerate + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + environments: Array + id: $.Scalars['ID'] + pulse: Object.EnvironmentPulse + workspace: Object.Workspace } export interface ProjectNode { - 'createdAt': $.Scalars['Date'] - 'displayName': $.Scalars['String'] - 'id': $.Scalars['String'] - 'workspaceId': $.Scalars['ID'] + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + id: $.Scalars['String'] + workspaceId: $.Scalars['ID'] } export interface PulseStatusDisabled { - 'enabled': $.Scalars['Boolean'] + enabled: $.Scalars['Boolean'] } export interface PulseStatusEnabled { - 'enabled': $.Scalars['Boolean'] - 'error': $.Scalars['String'] | null + enabled: $.Scalars['Boolean'] + error: $.Scalars['String'] | null } export interface ServiceKey { - 'createdAt': $.Scalars['Date'] - 'displayName': $.Scalars['String'] - 'id': $.Scalars['ID'] - 'valueHint': $.Scalars['String'] + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + id: $.Scalars['ID'] + valueHint: $.Scalars['String'] } export interface ServiceKeyNode { - 'displayName': $.Scalars['String'] - 'id': $.Scalars['String'] - 'valueHint': $.Scalars['String'] + displayName: $.Scalars['String'] + id: $.Scalars['String'] + valueHint: $.Scalars['String'] } export interface ServiceKeyWithValue { - 'serviceKey': Object.ServiceKey - 'value': $.Scalars['ID'] + serviceKey: Object.ServiceKey + value: $.Scalars['ID'] } export interface SideEffectConfirmation { - 'ok': $.Scalars['Boolean'] + ok: $.Scalars['Boolean'] } export interface StorageValue { - 'number': $.Scalars['Float'] - 'unit': Enum.StorageUnit | null + number: $.Scalars['Float'] + unit: Enum.StorageUnit | null } export interface StripeSubscriptionLineItem { - 'feature': Enum.FeatureHandle - 'id': $.Scalars['ID'] + feature: Enum.FeatureHandle + id: $.Scalars['ID'] } export interface System { - 'accelerate': Object.SystemAccelerate - 'plans': Array - 'pulse': Object.SystemPulse + accelerate: Object.SystemAccelerate + plans: Array + pulse: Object.SystemPulse } export interface SystemAccelerate { - 'defaultRegion': Object.SystemAccelerateRegion - 'regions': Array + defaultRegion: Object.SystemAccelerateRegion + regions: Array } export interface SystemAccelerateRegion { - 'displayName': $.Scalars['String'] - 'id': $.Scalars['ID'] + displayName: $.Scalars['String'] + id: $.Scalars['ID'] } export interface SystemPulse { - 'defaultRegion': Object.SystemAccelerateRegion - 'regions': Array + defaultRegion: Object.SystemAccelerateRegion + regions: Array } export interface TimeInterval { - 'from': $.Scalars['Date'] - 'to': $.Scalars['Date'] + from: $.Scalars['Date'] + to: $.Scalars['Date'] } export interface UsageProductAccelerate { - 'egress': Object.UsageProductAccelerateFeatureEgress - 'request': Object.UsageProductAccelerateFeatureRequest + egress: Object.UsageProductAccelerateFeatureEgress + request: Object.UsageProductAccelerateFeatureRequest } export interface UsageProductAccelerateFeatureEgress { - 'averageResponseSize': $.Scalars['Float'] - 'total': $.Scalars['Float'] + averageResponseSize: $.Scalars['Float'] + total: $.Scalars['Float'] } export interface UsageProductAccelerateFeatureRequest { - 'all': Object.UsageProductAccelerateFeatureRequestFilterAll - 'cacheHit': Object.UsageProductAccelerateFeatureRequestFilterCacheHit + all: Object.UsageProductAccelerateFeatureRequestFilterAll + cacheHit: Object.UsageProductAccelerateFeatureRequestFilterCacheHit } export interface UsageProductAccelerateFeatureRequestFilterAll { - 'count': $.Scalars['Int'] + count: $.Scalars['Int'] } export interface UsageProductAccelerateFeatureRequestFilterCacheHit { - 'ratioToMiss': $.Scalars['Int'] + ratioToMiss: $.Scalars['Int'] } export interface User { - 'displayName': $.Scalars['String'] | null - 'email': $.Scalars['String'] - 'featureFlags': Object.UserFeatureFlags - 'handle': $.Scalars['String'] | null - 'id': $.Scalars['ID'] - 'image': $.Scalars['String'] | null - 'preferences': Object.UserPreferences + displayName: $.Scalars['String'] | null + email: $.Scalars['String'] + featureFlags: Object.UserFeatureFlags + handle: $.Scalars['String'] | null + id: $.Scalars['ID'] + image: $.Scalars['String'] | null + preferences: Object.UserPreferences } export interface UserFeatureFlags { - 'adminDashboard': $.Scalars['Boolean'] - 'mars': $.Scalars['Boolean'] - 'mercury': $.Scalars['Boolean'] - 'venus': $.Scalars['Boolean'] + adminDashboard: $.Scalars['Boolean'] + mars: $.Scalars['Boolean'] + mercury: $.Scalars['Boolean'] + venus: $.Scalars['Boolean'] } export interface UserPreferences { - 'defaultWorkspace': Object.Workspace | null + defaultWorkspace: Object.Workspace | null } export interface Workspace { - 'billingAddress': Object.PhysicalAddress | null - 'billingEmail': $.Scalars['String'] - 'createdAt': $.Scalars['Date'] - 'displayName': $.Scalars['String'] - 'id': $.Scalars['ID'] - 'isUsersLastMembership': $.Scalars['Boolean'] - 'memberships': Array - 'paymentMethods': Array - 'planSubscription': Object.PlanSubscription - 'projects': Array - 'stripeCustomerId': $.Scalars['String'] - 'usage': Object.WorkspaceUsage + billingAddress: Object.PhysicalAddress | null + billingEmail: $.Scalars['String'] + createdAt: $.Scalars['Date'] + displayName: $.Scalars['String'] + id: $.Scalars['ID'] + isUsersLastMembership: $.Scalars['Boolean'] + memberships: Array + paymentMethods: Array + planSubscription: Object.PlanSubscription + projects: Array + stripeCustomerId: $.Scalars['String'] + usage: Object.WorkspaceUsage } export interface WorkspaceMembership { - 'id': $.Scalars['ID'] - 'role': Enum.WorkspaceRole - 'user': Object.User + id: $.Scalars['ID'] + role: Enum.WorkspaceRole + user: Object.User } export interface WorkspaceMembershipNode { - 'id': $.Scalars['String'] - 'workspaceId': $.Scalars['ID'] + id: $.Scalars['String'] + workspaceId: $.Scalars['ID'] } export interface WorkspaceNode { - 'billingEmail': $.Scalars['String'] - 'displayName': $.Scalars['String'] - 'id': $.Scalars['String'] + billingEmail: $.Scalars['String'] + displayName: $.Scalars['String'] + id: $.Scalars['String'] } export interface WorkspaceUsage { - 'accelerate': Object.UsageProductAccelerate - 'timeInterval': Object.TimeInterval + accelerate: Object.UsageProductAccelerate + timeInterval: Object.TimeInterval } } diff --git a/src/lib/Code.ts b/src/lib/Code.ts index 2a58d0c53..262a0065f 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -5,7 +5,7 @@ export namespace Code { export const union = (name: string, types: string[]) => `type ${name} =\n| ${Code.unionItems(types)}` export const unionItems = (types: string[]) => types.join(`\n| `) export const list = (type: string) => `Array<${type}>` - export const fieldType = (name: string, type: string) => `"${name}": ${type}` + export const fieldType = (name: string, type: string) => `${name}: ${type}` export const fieldTypes = (fieldTypes: string[]) => fieldTypes.join(`\n`) export const inter = (name: string, fields: string) => `interface ${name} {\n${fields}\n}` export const export$ = (thing: string) => `export ${thing}` From 104e0e259f6070ffc3be234d961eec80c540f204 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 17 Feb 2024 18:31:24 -0500 Subject: [PATCH 07/71] cli --- package.json | 2 +- src/cli/generate.ts | 6 ++++++ src/lib/builder.ts | 0 src/lib/generateTypes.ts | 27 +++++++++------------------ 4 files changed, 16 insertions(+), 19 deletions(-) create mode 100644 src/cli/generate.ts create mode 100644 src/lib/builder.ts diff --git a/package.json b/package.json index 4808886e6..90f96bc84 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ }, "homepage": "https://github.com/jasonkuhrt/graphql-request", "scripts": { - "demo": "tsx src/lib/generateTypes.ts && dprint fmt src/demo.ts", + "demo": "tsx src/cli/generate.ts && dprint fmt src/demo.ts", "dev": "rm -rf dist && tsc --watch", "format": "pnpm build:docs && dprint fmt", "lint": "eslint . --ext .ts,.tsx --fix", diff --git a/src/cli/generate.ts b/src/cli/generate.ts new file mode 100644 index 000000000..5158ef290 --- /dev/null +++ b/src/cli/generate.ts @@ -0,0 +1,6 @@ +import fs from 'node:fs' +import { generateSchemaTypes } from '../lib/generateTypes.js' + +const schemaSource = fs.readFileSync(`./examples/schema.graphql`, `utf8`) +const code = generateSchemaTypes({ schemaSource }) +fs.writeFileSync(`./src/demo.ts`, code, { encoding: `utf8` }) diff --git a/src/lib/builder.ts b/src/lib/builder.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/lib/generateTypes.ts b/src/lib/generateTypes.ts index 7209560f0..88fa8b0ef 100644 --- a/src/lib/generateTypes.ts +++ b/src/lib/generateTypes.ts @@ -1,4 +1,3 @@ -import { readFileSync } from 'fs' import type { GraphQLField, GraphQLInputField, @@ -6,23 +5,21 @@ import type { GraphQLInterfaceType, GraphQLNamedType, GraphQLObjectType, - GraphQLScalarType, } from 'graphql' import { GraphQLNonNull } from 'graphql' import { buildSchema } from 'graphql' -import fs from 'node:fs' import { Code } from './Code.js' import type { AnyClass, AnyNamedClassName, NameToClassNamedType } from './graphql.js' -import { getTypeMapByKind, NamedNameToClass, type NameToClass } from './graphql.js' +import { getTypeMapByKind, type NameToClass } from './graphql.js' import { entries } from './prelude.js' const namespaceNames = { - GraphQLEnumType: 'Enum', - GraphQLInputObjectType: 'InputObject', - GraphQLInterfaceType: 'Interface', - GraphQLObjectType: 'Object', - GraphQLScalarType: 'Scalar', - GraphQLUnionType: 'Union', + GraphQLEnumType: `Enum`, + GraphQLInputObjectType: `InputObject`, + GraphQLInterfaceType: `Interface`, + GraphQLObjectType: `Object`, + GraphQLScalarType: `Scalar`, + GraphQLUnionType: `Union`, } satisfies Record type AnyGraphQLField = GraphQLField | GraphQLInputField @@ -138,7 +135,7 @@ interface Input { schemaSource: string } -const generateSchemaTypes = (input: Input) => { +export const generateSchemaTypes = (input: Input) => { const schema = buildSchema(input.schemaSource) const typeMapByKind = getTypeMapByKind(schema) @@ -159,7 +156,7 @@ const generateSchemaTypes = (input: Input) => { for (const [name, types] of entries(typeMapByKind)) { if (name === `GraphQLScalarType`) continue - const namespaceName = name === 'GraphQLRootTypes' ? 'Root' : namespaceNames[name] + const namespaceName = name === `GraphQLRootTypes` ? `Root` : namespaceNames[name] code += Code.commentSectionTitle(namespaceName) code += `export namespace ${namespaceName} {\n` if (types.length === 0) { @@ -172,9 +169,3 @@ const generateSchemaTypes = (input: Input) => { return code } - -// demo - -const schemaSource = readFileSync(`./examples/schema.graphql`, `utf8`) -const code = generateSchemaTypes({ schemaSource }) -fs.writeFileSync(`./src/demo.ts`, code, { encoding: `utf8` }) From 369416ba01062fea402827f6c010941cceb7e272 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 17 Feb 2024 19:05:26 -0500 Subject: [PATCH 08/71] work --- src/demo.ts | 10 ++++-- src/lib/Code.ts | 3 +- src/lib/SelectionSet.ts | 24 +++++++++++++ src/lib/TSError.ts | 11 ++++++ src/lib/builder.ts | 12 +++++++ src/lib/generateTypes.ts | 77 ++++++++++++++++++++++------------------ src/lib/graphql.ts | 24 +++++++------ src/lib/prelude.ts | 3 +- 8 files changed, 113 insertions(+), 51 deletions(-) create mode 100644 src/lib/SelectionSet.ts create mode 100644 src/lib/TSError.ts diff --git a/src/demo.ts b/src/demo.ts index a104ec9b3..739b013b4 100644 --- a/src/demo.ts +++ b/src/demo.ts @@ -1,10 +1,10 @@ -namespace $ { +export namespace $ { export interface Scalars { Boolean: boolean Int: number String: string ID: string - Date: any + Date: string Float: number } } @@ -122,6 +122,9 @@ export namespace Root { } export interface Query { + /** + * testing + */ environment: | Object.Environment | Object.ErrorInternal @@ -702,6 +705,9 @@ export namespace Object { } export interface EnvironmentAccelerate { + /** + * Nullable. + */ databaseLink: Object.DatabaseLink | null holds: Array status: diff --git a/src/lib/Code.ts b/src/lib/Code.ts index 262a0065f..b95941660 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -7,8 +7,9 @@ export namespace Code { export const list = (type: string) => `Array<${type}>` export const fieldType = (name: string, type: string) => `${name}: ${type}` export const fieldTypes = (fieldTypes: string[]) => fieldTypes.join(`\n`) - export const inter = (name: string, fields: string) => `interface ${name} {\n${fields}\n}` + export const interface$ = (name: string, fields: string) => `interface ${name} {\n${fields}\n}` export const export$ = (thing: string) => `export ${thing}` + export const namespace = (name: string, content: string) => `namespace ${name} {\n${content}\n}` export const commentSectionTitle = (title: string) => { const lineSize = 60 const line = `-`.repeat(lineSize) diff --git a/src/lib/SelectionSet.ts b/src/lib/SelectionSet.ts new file mode 100644 index 000000000..0ce20eeb3 --- /dev/null +++ b/src/lib/SelectionSet.ts @@ -0,0 +1,24 @@ +import type * as Schema from '../demo.js' +import type { TSError } from './TSError.js' + +type ScalarType = Schema.$.Scalars[keyof Schema.$.Scalars] +type ObjectType = object +// type SelectableFieldsType = ObjectType + +type SelectionIndicator = boolean | 1 | 0 + +// todo if is a union type then must have each union member become an on_... field +// dprint-ignore +export type SelectionSet = + T extends ObjectType ? SelectionSetObject : + T extends ScalarType ? SelectionIndicator : + TSError<'SelectionSet', 'T is not a SelectableFieldsType',{ T:T }> + +// todo an empty selection set should be a static type error +type SelectionSetObject<$ObjectType extends ObjectType> = + & { + [Key in keyof $ObjectType]?: SelectionSet> + } + & { + __typename?: SelectionIndicator + } diff --git a/src/lib/TSError.ts b/src/lib/TSError.ts new file mode 100644 index 000000000..fd131382b --- /dev/null +++ b/src/lib/TSError.ts @@ -0,0 +1,11 @@ +export const TypeErrorSymbol = Symbol(`TypeError`) + +export type TSError< + Location extends string, + Message extends string, + Context extends Record = never, +> = { + [TypeErrorSymbol]: true + message: `Error (${Location}): ${Message}` + context: Context +} diff --git a/src/lib/builder.ts b/src/lib/builder.ts index e69de29bb..abbc379f9 100644 --- a/src/lib/builder.ts +++ b/src/lib/builder.ts @@ -0,0 +1,12 @@ +import type * as Schema from '../demo.js' +import type { SelectionSet } from './SelectionSet.js' + +type Query = (document: SelectionSet) => Promise + +declare const query: Query + +await query({ + environment: { + + }, +}) diff --git a/src/lib/generateTypes.ts b/src/lib/generateTypes.ts index 88fa8b0ef..c00a53146 100644 --- a/src/lib/generateTypes.ts +++ b/src/lib/generateTypes.ts @@ -1,3 +1,6 @@ +/** + * Emit JSDoc from GraphQL descriptions + */ import type { GraphQLField, GraphQLInputField, @@ -29,24 +32,26 @@ type AnyGraphQLField = GraphQLField | GraphQLInputField type AnyGraphQLFieldsType = GraphQLObjectType | GraphQLInterfaceType | GraphQLInputObjectType -const definePointerRenderers = <$Renderers extends { [ClassName in keyof NameToClass]: any }>(renderers: { - [ClassName in keyof $Renderers]: ( - node: ClassName extends keyof NameToClass ? InstanceType : never, - ) => string -}) => renderers +const definePointerRenderers = <$Renderers extends { [ClassName in keyof NameToClass]: any }>( + renderers: { + [ClassName in keyof $Renderers]: ( + node: ClassName extends keyof NameToClass ? InstanceType : never, + ) => string + }, +) => renderers const defineConcreteRenderers = < $Renderers extends { [ClassName in keyof NameToClassNamedType]: any }, ->(renderers: { - [ClassName in keyof $Renderers]: ( - node: ClassName extends keyof NameToClassNamedType - ? InstanceType - : never, - ) => string -}): { +>( + renderers: { + [ClassName in keyof $Renderers]: ( + node: ClassName extends keyof NameToClassNamedType ? InstanceType + : never, + ) => string + }, +): { [ClassName in keyof $Renderers]: ( - node: ClassName extends keyof NameToClass - ? InstanceType | null | undefined + node: ClassName extends keyof NameToClass ? InstanceType | null | undefined : never, ) => string } => { @@ -96,9 +101,9 @@ const concreteRenderers = defineConcreteRenderers({ node.getValues().map((_) => Code.quote(_.name)), ), ), - GraphQLInputObjectType: (node) => Code.export$(Code.inter(node.name, renderFields(node))), - GraphQLInterfaceType: (node) => Code.export$(Code.inter(node.name, renderFields(node))), - GraphQLObjectType: (node) => Code.export$(Code.inter(node.name, renderFields(node))), + GraphQLInputObjectType: (node) => Code.export$(Code.interface$(node.name, renderFields(node))), + GraphQLInterfaceType: (node) => Code.export$(Code.interface$(node.name, renderFields(node))), + GraphQLObjectType: (node) => Code.export$(Code.interface$(node.name, renderFields(node))), GraphQLScalarType: () => ``, GraphQLUnionType: (node) => Code.export$( @@ -116,8 +121,7 @@ const renderFields = (node: AnyGraphQLFieldsType): string => { } const renderField = (field: AnyGraphQLField): string => { - const [fieldType, nullable] = - field.type instanceof GraphQLNonNull ? [field.type.ofType, false] : [field.type, true] + const [fieldType, nullable] = field.type instanceof GraphQLNonNull ? [field.type.ofType, false] : [field.type, true] return nullable ? Code.nullable(dispatchToPointerRenderer(fieldType)) : dispatchToPointerRenderer(fieldType) // eslint-disable-line } @@ -141,30 +145,33 @@ export const generateSchemaTypes = (input: Input) => { let code = `` - code += ` - namespace $ { - export interface Scalars { - ${typeMapByKind.GraphQLScalarType.map((_) => { + code += Code.export$(Code.namespace( + `$`, + Code.export$(Code.interface$( + `Scalars`, + ` + ${ + typeMapByKind.GraphQLScalarType.map((_) => { // todo strict mode where instead of falling back to "any" we throw an error - const type = scalarTypeMap[_.name] || `any` + const type = scalarTypeMap[_.name] || `string` return Code.fieldType(_.name, type) - }).join(`\n`)} - } - } - ` + }).join(`\n`) + } + `, + )), + )) for (const [name, types] of entries(typeMapByKind)) { if (name === `GraphQLScalarType`) continue const namespaceName = name === `GraphQLRootTypes` ? `Root` : namespaceNames[name] code += Code.commentSectionTitle(namespaceName) - code += `export namespace ${namespaceName} {\n` - if (types.length === 0) { - code += `// -- no types --\n` - continue - } - code += types.map(dispatchToConcreteRenderer).join(`\n\n`) - code += `}` + code += Code.export$(Code.namespace( + namespaceName, + types.length === 0 + ? `// -- no types --\n` + : types.map(dispatchToConcreteRenderer).join(`\n\n`), + )) } return code diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index c0b690e2c..459d2f27b 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -13,17 +13,19 @@ import { export const getTypeMapByKind = (schema: GraphQLSchema) => { const typeMap = schema.getTypeMap() const typeMapValues = Object.values(typeMap) - const typeMapByKind: { - [Name in keyof NameToClassNamedType]: InstanceType[] - } & { GraphQLRootTypes: GraphQLObjectType[] } = { - GraphQLRootTypes: [], - GraphQLScalarType: [], - GraphQLEnumType: [], - GraphQLInputObjectType: [], - GraphQLInterfaceType: [], - GraphQLObjectType: [], - GraphQLUnionType: [], - } + const typeMapByKind: + & { + [Name in keyof NameToClassNamedType]: InstanceType[] + } + & { GraphQLRootTypes: GraphQLObjectType[] } = { + GraphQLRootTypes: [], + GraphQLScalarType: [], + GraphQLEnumType: [], + GraphQLInputObjectType: [], + GraphQLInterfaceType: [], + GraphQLObjectType: [], + GraphQLUnionType: [], + } for (const type of typeMapValues) { if (type.name.startsWith(`__`)) continue switch (true) { diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index e73bd3fc8..f459ef38c 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -84,5 +84,4 @@ export const isPlainObject = (value: unknown): value is object => { return typeof value === `object` && value !== null && !Array.isArray(value) } -export const entries = >(obj: T) => - Object.entries(obj) as [keyof T, T[keyof T]][] +export const entries = >(obj: T) => Object.entries(obj) as [keyof T, T[keyof T]][] From 7173079b29280b147792a3219ac66c924c4fc209 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 17 Feb 2024 21:45:00 -0500 Subject: [PATCH 09/71] builder tests --- tests/builder/selectionSet.test.d.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/builder/selectionSet.test.d.ts diff --git a/tests/builder/selectionSet.test.d.ts b/tests/builder/selectionSet.test.d.ts new file mode 100644 index 000000000..e69de29bb From 8bc622bb4dd9f6ac53e264c7c79ff001443be97b Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 18 Feb 2024 00:04:59 -0500 Subject: [PATCH 10/71] work --- src/cli/generate.ts | 10 +- src/lib/Code.ts | 1 + src/lib/SelectionSet.ts | 42 +++++---- src/lib/builder.ts | 13 +-- src/lib/generateTypes.ts | 65 +++++++++---- src/lib/schemaTypes.ts | 14 +++ tests/builder/_/schema.graphql | 23 +++++ tests/builder/_/schema.ts | 90 ++++++++++++++++++ .../__snapshots__/generate.test.ts.snap | 94 +++++++++++++++++++ tests/builder/generate.test.ts | 11 +++ tests/builder/selectionSet.test.d.ts | 0 tests/builder/types/selectionSet.test-d.ts | 24 +++++ 12 files changed, 338 insertions(+), 49 deletions(-) create mode 100644 src/lib/schemaTypes.ts create mode 100644 tests/builder/_/schema.graphql create mode 100644 tests/builder/_/schema.ts create mode 100644 tests/builder/__snapshots__/generate.test.ts.snap create mode 100644 tests/builder/generate.test.ts delete mode 100644 tests/builder/selectionSet.test.d.ts create mode 100644 tests/builder/types/selectionSet.test-d.ts diff --git a/src/cli/generate.ts b/src/cli/generate.ts index 5158ef290..ff6f96a6d 100644 --- a/src/cli/generate.ts +++ b/src/cli/generate.ts @@ -1,6 +1,6 @@ -import fs from 'node:fs' -import { generateSchemaTypes } from '../lib/generateTypes.js' +import { generateFile } from '../lib/generateTypes.js' -const schemaSource = fs.readFileSync(`./examples/schema.graphql`, `utf8`) -const code = generateSchemaTypes({ schemaSource }) -fs.writeFileSync(`./src/demo.ts`, code, { encoding: `utf8` }) +await generateFile({ + schemaPath: `./examples/schema.graphql`, + typeScriptPath: `./src/demo.ts`, +}) diff --git a/src/lib/Code.ts b/src/lib/Code.ts index b95941660..4094887b1 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -10,6 +10,7 @@ export namespace Code { export const interface$ = (name: string, fields: string) => `interface ${name} {\n${fields}\n}` export const export$ = (thing: string) => `export ${thing}` export const namespace = (name: string, content: string) => `namespace ${name} {\n${content}\n}` + export const group = (...content: string[]) => content.join(`\n`) export const commentSectionTitle = (title: string) => { const lineSize = 60 const line = `-`.repeat(lineSize) diff --git a/src/lib/SelectionSet.ts b/src/lib/SelectionSet.ts index 0ce20eeb3..9e94f62c6 100644 --- a/src/lib/SelectionSet.ts +++ b/src/lib/SelectionSet.ts @@ -1,24 +1,34 @@ -import type * as Schema from '../demo.js' +import type { + ExcludeNull, + Indicator, + Metadata, + ObjectType, + OmitUnionBrand, + ScalarType, + UnionType, +} from './schemaTypes.js' import type { TSError } from './TSError.js' -type ScalarType = Schema.$.Scalars[keyof Schema.$.Scalars] -type ObjectType = object -// type SelectableFieldsType = ObjectType - -type SelectionIndicator = boolean | 1 | 0 - // todo if is a union type then must have each union member become an on_... field // dprint-ignore -export type SelectionSet = - T extends ObjectType ? SelectionSetObject : - T extends ScalarType ? SelectionIndicator : - TSError<'SelectionSet', 'T is not a SelectableFieldsType',{ T:T }> +export type SelectionSet = + T extends ScalarType ? Indicator : + ExcludeNull extends UnionType ? SelectionSetUnion, $Metadata> & { __typename?: Indicator } : + T extends ObjectType ? SelectionSetObject : + TSError<'SelectionSet', 'T is not a SelectableFieldsType',{ T:T }> // todo an empty selection set should be a static type error -type SelectionSetObject<$ObjectType extends ObjectType> = - & { - [Key in keyof $ObjectType]?: SelectionSet> - } +// dprint-ignore +type SelectionSetObject<$ObjectType extends ObjectType, $Metadata extends Metadata> = { + [Key in keyof $ObjectType]?: SelectionSet, $Metadata> +} + +// TODO why does $object no get passed to this in a distributed way? +type SelectionSetUnion<$Object extends ObjectType, $Metadata extends Metadata> = & { - __typename?: SelectionIndicator + [Key in $Object['__typename'] as `on${Capitalize}`]?: SelectionSet< + OmitUnionBrand>, + $Metadata + > } + & { __typename?: Indicator } diff --git a/src/lib/builder.ts b/src/lib/builder.ts index abbc379f9..796c7f1e2 100644 --- a/src/lib/builder.ts +++ b/src/lib/builder.ts @@ -1,12 +1,7 @@ -import type * as Schema from '../demo.js' import type { SelectionSet } from './SelectionSet.js' -type Query = (document: SelectionSet) => Promise +export type Query = <$Query>(document: SelectionSet<$Query>) => Promise -declare const query: Query - -await query({ - environment: { - - }, -}) +export const query: Query = async (document) => { + // todo +} diff --git a/src/lib/generateTypes.ts b/src/lib/generateTypes.ts index c00a53146..d4546823c 100644 --- a/src/lib/generateTypes.ts +++ b/src/lib/generateTypes.ts @@ -1,16 +1,14 @@ -/** - * Emit JSDoc from GraphQL descriptions - */ +// todo Emit JSDoc from GraphQL descriptions import type { GraphQLField, GraphQLInputField, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLNamedType, - GraphQLObjectType, } from 'graphql' -import { GraphQLNonNull } from 'graphql' +import { GraphQLNonNull, GraphQLObjectType } from 'graphql' import { buildSchema } from 'graphql' +import fs from 'node:fs/promises' import { Code } from './Code.js' import type { AnyClass, AnyNamedClassName, NameToClassNamedType } from './graphql.js' import { getTypeMapByKind, type NameToClass } from './graphql.js' @@ -90,7 +88,7 @@ const pointerRenderers = definePointerRenderers({ GraphQLList: (node) => Code.list(dispatchToPointerRenderer(node.ofType)), GraphQLObjectType: (node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), GraphQLScalarType: (node) => `$.Scalars[${Code.quote(node.name)}]`, - GraphQLUnionType: (node) => Code.unionItems(node.getTypes().map((_) => dispatchToPointerRenderer(_))), + GraphQLUnionType: (node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), }) const concreteRenderers = defineConcreteRenderers({ @@ -101,7 +99,11 @@ const concreteRenderers = defineConcreteRenderers({ node.getValues().map((_) => Code.quote(_.name)), ), ), - GraphQLInputObjectType: (node) => Code.export$(Code.interface$(node.name, renderFields(node))), + GraphQLInputObjectType: (node) => + Code.export$(Code.interface$( + node.name, + renderFields(node), + )), GraphQLInterfaceType: (node) => Code.export$(Code.interface$(node.name, renderFields(node))), GraphQLObjectType: (node) => Code.export$(Code.interface$(node.name, renderFields(node))), GraphQLScalarType: () => ``, @@ -109,14 +111,17 @@ const concreteRenderers = defineConcreteRenderers({ Code.export$( Code.union( node.name, - node.getTypes().map((_) => dispatchToPointerRenderer(_)), + node.getTypes().map((_) => dispatchToPointerRenderer(_) + `& { $$union:true}`), ), ), }) const renderFields = (node: AnyGraphQLFieldsType): string => { return Code.fieldTypes( - Object.values(node.getFields()).map((field) => Code.fieldType(field.name, renderField(field))), + [ + ...(node instanceof GraphQLObjectType ? [Code.fieldType(`__typename`, `"${node.name}"`)] : []), + ...Object.values(node.getFields()).map((field) => Code.fieldType(field.name, renderField(field))), + ], ) } @@ -147,18 +152,34 @@ export const generateSchemaTypes = (input: Input) => { code += Code.export$(Code.namespace( `$`, - Code.export$(Code.interface$( - `Scalars`, - ` + Code.group( + Code.export$( + Code.interface$( + `Metadata`, + Code.fieldTypes([ + Code.fieldType( + `unions`, + typeMapByKind.GraphQLUnionType.length > 0 + ? Code.unionItems(typeMapByKind.GraphQLUnionType.map(_ => `Union.${_.name}`)) + : `null`, + ), + Code.fieldType(`scalars`, `Scalars`), + ]), + ), + ), + Code.export$(Code.interface$( + `Scalars`, + ` ${ - typeMapByKind.GraphQLScalarType.map((_) => { - // todo strict mode where instead of falling back to "any" we throw an error - const type = scalarTypeMap[_.name] || `string` - return Code.fieldType(_.name, type) - }).join(`\n`) - } + typeMapByKind.GraphQLScalarType.map((_) => { + // todo strict mode where instead of falling back to "any" we throw an error + const type = scalarTypeMap[_.name] || `string` + return Code.fieldType(_.name, type) + }).join(`\n`) + } `, - )), + )), + ), )) for (const [name, types] of entries(typeMapByKind)) { @@ -176,3 +197,9 @@ export const generateSchemaTypes = (input: Input) => { return code } + +export const generateFile = async (params: { schemaPath: string; typeScriptPath: string }) => { + const schemaSource = await fs.readFile(params.schemaPath, `utf8`) + const code = generateSchemaTypes({ schemaSource }) + await fs.writeFile(params.typeScriptPath, code, { encoding: `utf8` }) +} diff --git a/src/lib/schemaTypes.ts b/src/lib/schemaTypes.ts new file mode 100644 index 000000000..27c159397 --- /dev/null +++ b/src/lib/schemaTypes.ts @@ -0,0 +1,14 @@ +export interface Metadata { + unions: null | ObjectType + scalars: object +} + +// todo needs to be extensible for custom scalars... +export type ScalarType = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] +export type ObjectType = { __typename: string } +export type UnionType = ObjectType & { $$union: true } + +export type ExcludeNull = Exclude +export type OmitUnionBrand = Omit + +export type Indicator = boolean | 1 | 0 diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql new file mode 100644 index 000000000..a18a6f070 --- /dev/null +++ b/tests/builder/_/schema.graphql @@ -0,0 +1,23 @@ +type Query { + string: String + scalars: Scalars + fooBarUnion: FooBarUnion +} + +union FooBarUnion = Foo | Bar + +type Foo { + a: String +} + +type Bar { + b: Int +} + +type Scalars { + a: String + b: Int + c: Float + d: Boolean + e: ID +} diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts new file mode 100644 index 000000000..90cdde24a --- /dev/null +++ b/tests/builder/_/schema.ts @@ -0,0 +1,90 @@ +export namespace $ { +export interface Metadata { +unions: Union.FooBarUnion +scalars: Scalars +} +export interface Scalars { + + String: string +Int: number +Float: number +Boolean: boolean +ID: string + +} +} + +// ------------------------------------------------------------ // +// Root // +// ------------------------------------------------------------ // + +export namespace Root { +export interface Query { +__typename: "Query" +string: $.Scalars["String"] | null +scalars: Object.Scalars | null +fooBarUnion: Union.FooBarUnion | null +} +} + +// ------------------------------------------------------------ // +// Enum // +// ------------------------------------------------------------ // + +export namespace Enum { +// -- no types -- + +} + +// ------------------------------------------------------------ // +// InputObject // +// ------------------------------------------------------------ // + +export namespace InputObject { +// -- no types -- + +} + +// ------------------------------------------------------------ // +// Interface // +// ------------------------------------------------------------ // + +export namespace Interface { +// -- no types -- + +} + +// ------------------------------------------------------------ // +// Object // +// ------------------------------------------------------------ // + +export namespace Object { +export interface Foo { +__typename: "Foo" +a: $.Scalars["String"] | null +} + +export interface Bar { +__typename: "Bar" +b: $.Scalars["Int"] | null +} + +export interface Scalars { +__typename: "Scalars" +a: $.Scalars["String"] | null +b: $.Scalars["Int"] | null +c: $.Scalars["Float"] | null +d: $.Scalars["Boolean"] | null +e: $.Scalars["ID"] | null +} +} + +// ------------------------------------------------------------ // +// Union // +// ------------------------------------------------------------ // + +export namespace Union { +export type FooBarUnion = +| Object.Foo& { $$union:true} +| Object.Bar& { $$union:true} +} \ No newline at end of file diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap new file mode 100644 index 000000000..ed3647b69 --- /dev/null +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -0,0 +1,94 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`generates types from GraphQL SDL file 1`] = ` +"export namespace $ { +export interface Metadata { +unions: Union.FooBarUnion +scalars: Scalars +} +export interface Scalars { + + String: string +Int: number +Float: number +Boolean: boolean +ID: string + +} +} + +// ------------------------------------------------------------ // +// Root // +// ------------------------------------------------------------ // + +export namespace Root { +export interface Query { +__typename: "Query" +string: $.Scalars["String"] | null +scalars: Object.Scalars | null +fooBarUnion: Union.FooBarUnion | null +} +} + +// ------------------------------------------------------------ // +// Enum // +// ------------------------------------------------------------ // + +export namespace Enum { +// -- no types -- + +} + +// ------------------------------------------------------------ // +// InputObject // +// ------------------------------------------------------------ // + +export namespace InputObject { +// -- no types -- + +} + +// ------------------------------------------------------------ // +// Interface // +// ------------------------------------------------------------ // + +export namespace Interface { +// -- no types -- + +} + +// ------------------------------------------------------------ // +// Object // +// ------------------------------------------------------------ // + +export namespace Object { +export interface Foo { +__typename: "Foo" +a: $.Scalars["String"] | null +} + +export interface Bar { +__typename: "Bar" +b: $.Scalars["Int"] | null +} + +export interface Scalars { +__typename: "Scalars" +a: $.Scalars["String"] | null +b: $.Scalars["Int"] | null +c: $.Scalars["Float"] | null +d: $.Scalars["Boolean"] | null +e: $.Scalars["ID"] | null +} +} + +// ------------------------------------------------------------ // +// Union // +// ------------------------------------------------------------ // + +export namespace Union { +export type FooBarUnion = +| Object.Foo& { $$union:true} +| Object.Bar& { $$union:true} +}" +`; diff --git a/tests/builder/generate.test.ts b/tests/builder/generate.test.ts new file mode 100644 index 000000000..203a780f7 --- /dev/null +++ b/tests/builder/generate.test.ts @@ -0,0 +1,11 @@ +import { readFile } from 'fs/promises' +import { expect, test } from 'vitest' +import { generateFile } from '../../src/lib/generateTypes.js' + +test(`generates types from GraphQL SDL file`, async () => { + await generateFile({ + schemaPath: `./tests/builder/_/schema.graphql`, + typeScriptPath: `./tests/builder/_/schema.ts`, + }) + expect(await readFile(`./tests/builder/_/schema.ts`, `utf8`)).toMatchSnapshot() +}) diff --git a/tests/builder/selectionSet.test.d.ts b/tests/builder/selectionSet.test.d.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/builder/types/selectionSet.test-d.ts b/tests/builder/types/selectionSet.test-d.ts new file mode 100644 index 000000000..9d09c6071 --- /dev/null +++ b/tests/builder/types/selectionSet.test-d.ts @@ -0,0 +1,24 @@ +import { assertType, test } from 'vitest' +import type { SelectionSet } from '../../../src/lib/SelectionSet.js' +import type * as Schema from '../_/schema.js' + +type S = SelectionSet + +test(`general`, () => { + assertType({ string: true }) + assertType({ string: false }) + assertType({ string: 1 }) + assertType({ string: 0 }) + assertType({ string: undefined }) + assertType({ __typename: true }) + assertType({ scalars: { a: true } }) + assertType({ fooBarUnion: { __typename: true } }) + assertType({ fooBarUnion: { onFoo: { __typename: true } } }) + assertType({ fooBarUnion: { onFoo: { a: true } } }) + // @ts-expect-error no b + assertType({ fooBarUnion: { onFoo: { b: true } } }) + assertType({ fooBarUnion: { onBar: { __typename: true } } }) + assertType({ fooBarUnion: { onBar: { b: true } } }) + // @ts-expect-error no a + assertType({ fooBarUnion: { onBar: { a: true } } }) +}) From a664e5700ebec4f9b9b3692f887ab54467e531e2 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 18 Feb 2024 00:24:37 -0500 Subject: [PATCH 11/71] work --- src/lib/Code.ts | 9 ++-- src/lib/SelectionSet.ts | 16 +++---- src/lib/builder.ts | 8 +++- src/lib/generateTypes.ts | 45 ++++++++++++++----- src/lib/prelude.ts | 2 + src/lib/schemaTypes.ts | 12 ++++- tests/builder/_/schema.ts | 11 ++++- .../__snapshots__/generate.test.ts.snap | 11 ++++- tests/builder/types/selectionSet.test-d.ts | 2 +- 9 files changed, 84 insertions(+), 32 deletions(-) diff --git a/src/lib/Code.ts b/src/lib/Code.ts index 4094887b1..30740350a 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -5,11 +5,12 @@ export namespace Code { export const union = (name: string, types: string[]) => `type ${name} =\n| ${Code.unionItems(types)}` export const unionItems = (types: string[]) => types.join(`\n| `) export const list = (type: string) => `Array<${type}>` - export const fieldType = (name: string, type: string) => `${name}: ${type}` - export const fieldTypes = (fieldTypes: string[]) => fieldTypes.join(`\n`) - export const interface$ = (name: string, fields: string) => `interface ${name} {\n${fields}\n}` + export const field = (name: string, type: string) => `${name}: ${type}` + export const fields = (fieldTypes: string[]) => fieldTypes.join(`\n`) + export const object = (fields: string) => `{\n${fields}\n}` + export const interface$ = (name: string, fields: string) => `interface ${name} ${Code.object(fields)}` export const export$ = (thing: string) => `export ${thing}` - export const namespace = (name: string, content: string) => `namespace ${name} {\n${content}\n}` + export const namespace = (name: string, content: string) => `namespace ${name} ${Code.object(content)}` export const group = (...content: string[]) => content.join(`\n`) export const commentSectionTitle = (title: string) => { const lineSize = 60 diff --git a/src/lib/SelectionSet.ts b/src/lib/SelectionSet.ts index 9e94f62c6..1c56f5cba 100644 --- a/src/lib/SelectionSet.ts +++ b/src/lib/SelectionSet.ts @@ -1,34 +1,34 @@ import type { ExcludeNull, Indicator, - Metadata, ObjectType, OmitUnionBrand, ScalarType, + SchemaIndex, UnionType, } from './schemaTypes.js' import type { TSError } from './TSError.js' // todo if is a union type then must have each union member become an on_... field // dprint-ignore -export type SelectionSet = +export type SelectionSet = T extends ScalarType ? Indicator : - ExcludeNull extends UnionType ? SelectionSetUnion, $Metadata> & { __typename?: Indicator } : - T extends ObjectType ? SelectionSetObject : + ExcludeNull extends UnionType ? SelectionSetUnion, $Index> & { __typename?: Indicator } : + T extends ObjectType ? SelectionSetObject : TSError<'SelectionSet', 'T is not a SelectableFieldsType',{ T:T }> // todo an empty selection set should be a static type error // dprint-ignore -type SelectionSetObject<$ObjectType extends ObjectType, $Metadata extends Metadata> = { - [Key in keyof $ObjectType]?: SelectionSet, $Metadata> +type SelectionSetObject<$ObjectType extends ObjectType, $Index extends SchemaIndex> = { + [Key in keyof $ObjectType]?: SelectionSet, $Index> } // TODO why does $object no get passed to this in a distributed way? -type SelectionSetUnion<$Object extends ObjectType, $Metadata extends Metadata> = +type SelectionSetUnion<$Object extends ObjectType, $Index extends SchemaIndex> = & { [Key in $Object['__typename'] as `on${Capitalize}`]?: SelectionSet< OmitUnionBrand>, - $Metadata + $Index > } & { __typename?: Indicator } diff --git a/src/lib/builder.ts b/src/lib/builder.ts index 796c7f1e2..e421528c3 100644 --- a/src/lib/builder.ts +++ b/src/lib/builder.ts @@ -1,7 +1,13 @@ +import type { SchemaIndex } from './schemaTypes.js' import type { SelectionSet } from './SelectionSet.js' -export type Query = <$Query>(document: SelectionSet<$Query>) => Promise +export type Query = <$SchemaIndex extends SchemaIndex>( + document: $SchemaIndex['Root']['Query'] extends null ? null + : SelectionSet<$SchemaIndex['Root']['Query'], $SchemaIndex>, +) => Promise export const query: Query = async (document) => { // todo + document + await Promise.resolve() } diff --git a/src/lib/generateTypes.ts b/src/lib/generateTypes.ts index d4546823c..62c318e60 100644 --- a/src/lib/generateTypes.ts +++ b/src/lib/generateTypes.ts @@ -12,7 +12,7 @@ import fs from 'node:fs/promises' import { Code } from './Code.js' import type { AnyClass, AnyNamedClassName, NameToClassNamedType } from './graphql.js' import { getTypeMapByKind, type NameToClass } from './graphql.js' -import { entries } from './prelude.js' +import { entries, values } from './prelude.js' const namespaceNames = { GraphQLEnumType: `Enum`, @@ -117,10 +117,10 @@ const concreteRenderers = defineConcreteRenderers({ }) const renderFields = (node: AnyGraphQLFieldsType): string => { - return Code.fieldTypes( + return Code.fields( [ - ...(node instanceof GraphQLObjectType ? [Code.fieldType(`__typename`, `"${node.name}"`)] : []), - ...Object.values(node.getFields()).map((field) => Code.fieldType(field.name, renderField(field))), + ...(node instanceof GraphQLObjectType ? [Code.field(`__typename`, `"${node.name}"`)] : []), + ...values(node.getFields()).map((field) => Code.field(field.name, renderField(field))), ], ) } @@ -150,20 +150,41 @@ export const generateSchemaTypes = (input: Input) => { let code = `` + const hasQuery = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Query`) + const hasMutation = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Mutation`) + const hasSubscription = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Subscription`) + code += Code.export$(Code.namespace( `$`, Code.group( Code.export$( Code.interface$( - `Metadata`, - Code.fieldTypes([ - Code.fieldType( + `Index`, + Code.fields([ + Code.field( + `Root`, + Code.object( + Code.fields([ + Code.field(`Query`, hasQuery ? `Root.Query` : `null`), + Code.field(`Mutation`, hasMutation ? `Root.Mutation` : `null`), + Code.field(`Subscription`, hasSubscription ? `Root.Subscription` : `null`), + ]), + ), + ), + Code.field( `unions`, - typeMapByKind.GraphQLUnionType.length > 0 - ? Code.unionItems(typeMapByKind.GraphQLUnionType.map(_ => `Union.${_.name}`)) - : `null`, + Code.object( + Code.fields([ + Code.field( + `Union`, + typeMapByKind.GraphQLUnionType.length > 0 + ? Code.unionItems(typeMapByKind.GraphQLUnionType.map(_ => `Union.${_.name}`)) + : `null`, + ), + ]), + ), ), - Code.fieldType(`scalars`, `Scalars`), + Code.field(`scalars`, `Scalars`), ]), ), ), @@ -174,7 +195,7 @@ export const generateSchemaTypes = (input: Input) => { typeMapByKind.GraphQLScalarType.map((_) => { // todo strict mode where instead of falling back to "any" we throw an error const type = scalarTypeMap[_.name] || `string` - return Code.fieldType(_.name, type) + return Code.field(_.name, type) }).join(`\n`) } `, diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index f459ef38c..d37381134 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -85,3 +85,5 @@ export const isPlainObject = (value: unknown): value is object => { } export const entries = >(obj: T) => Object.entries(obj) as [keyof T, T[keyof T]][] + +export const values = >(obj: T): T[keyof T][] => Object.values(obj) as T[keyof T][] diff --git a/src/lib/schemaTypes.ts b/src/lib/schemaTypes.ts index 27c159397..6d6183a1d 100644 --- a/src/lib/schemaTypes.ts +++ b/src/lib/schemaTypes.ts @@ -1,5 +1,13 @@ -export interface Metadata { - unions: null | ObjectType +export interface SchemaIndex { + unions: { + Union: null | ObjectType + // root: null | ObjectType + } + Root: { + Query: null | ObjectType + Mutation: null | ObjectType + Subscription: null | ObjectType + } scalars: object } diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 90cdde24a..516ed6141 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -1,6 +1,13 @@ export namespace $ { -export interface Metadata { -unions: Union.FooBarUnion +export interface Index { +Root: { +Query: Root.Query +Mutation: null +Subscription: null +} +unions: { +Union: Union.FooBarUnion +} scalars: Scalars } export interface Scalars { diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index ed3647b69..6973635a1 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -2,8 +2,15 @@ exports[`generates types from GraphQL SDL file 1`] = ` "export namespace $ { -export interface Metadata { -unions: Union.FooBarUnion +export interface Index { +Root: { +Query: Root.Query +Mutation: null +Subscription: null +} +unions: { +Union: Union.FooBarUnion +} scalars: Scalars } export interface Scalars { diff --git a/tests/builder/types/selectionSet.test-d.ts b/tests/builder/types/selectionSet.test-d.ts index 9d09c6071..a7276b97a 100644 --- a/tests/builder/types/selectionSet.test-d.ts +++ b/tests/builder/types/selectionSet.test-d.ts @@ -2,7 +2,7 @@ import { assertType, test } from 'vitest' import type { SelectionSet } from '../../../src/lib/SelectionSet.js' import type * as Schema from '../_/schema.js' -type S = SelectionSet +type S = SelectionSet test(`general`, () => { assertType({ string: true }) From 405c6da3cb02984611da64eb1c76f9edad7502cd Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 18 Feb 2024 14:14:02 -0500 Subject: [PATCH 12/71] descriptions and deprecated to TSDoc --- src/cli/generate.ts | 2 +- src/lib/Code.ts | 4 + src/lib/generate.ts | 289 ++++++++++++++++++ src/lib/generateTypes.ts | 226 -------------- src/lib/graphql.ts | 65 +++- tests/builder/_/schema.graphql | 27 +- tests/builder/_/schema.ts | 26 +- .../__snapshots__/generate.test.ts.snap | 26 +- tests/builder/generate.test.ts | 2 +- 9 files changed, 433 insertions(+), 234 deletions(-) create mode 100644 src/lib/generate.ts delete mode 100644 src/lib/generateTypes.ts diff --git a/src/cli/generate.ts b/src/cli/generate.ts index ff6f96a6d..282985b50 100644 --- a/src/cli/generate.ts +++ b/src/cli/generate.ts @@ -1,4 +1,4 @@ -import { generateFile } from '../lib/generateTypes.js' +import { generateFile } from '../lib/generate.js' await generateFile({ schemaPath: `./examples/schema.graphql`, diff --git a/src/lib/Code.ts b/src/lib/Code.ts index 30740350a..f37ea9dce 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -10,6 +10,8 @@ export namespace Code { export const object = (fields: string) => `{\n${fields}\n}` export const interface$ = (name: string, fields: string) => `interface ${name} ${Code.object(fields)}` export const export$ = (thing: string) => `export ${thing}` + export const TSDoc = (content: string | null, block: string) => + content === null ? block : `/**\n${prependLines(`* `, content) || `*`}\n*/\n${block}` export const namespace = (name: string, content: string) => `namespace ${name} ${Code.object(content)}` export const group = (...content: string[]) => content.join(`\n`) export const commentSectionTitle = (title: string) => { @@ -20,3 +22,5 @@ export namespace Code { return `\n\n// ${line} //\n// ${titlePrefixSpace + title + titleSuffixSpace} //\n// ${line} //\n\n` } } + +const prependLines = (prepend: string, str: string) => str.split(`\n`).map((line) => `${prepend}${line}`).join(`\n`) diff --git a/src/lib/generate.ts b/src/lib/generate.ts new file mode 100644 index 000000000..8be49887e --- /dev/null +++ b/src/lib/generate.ts @@ -0,0 +1,289 @@ +// todo Emit JSDoc from GraphQL descriptions +import type { GraphQLEnumValue, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLNamedType } from 'graphql' +import { GraphQLNonNull, GraphQLObjectType, isEnumType } from 'graphql' +import { buildSchema } from 'graphql' +import _ from 'json-bigint' +import fs from 'node:fs/promises' +import { Code } from './Code.js' +import type { AnyClass, AnyField, AnyNamedClassName, Describable, NameToClassNamedType } from './graphql.js' +import { getNodeDisplayName, getTypeMapByKind, isDeprecatableNode, type NameToClass } from './graphql.js' +import { entries, values } from './prelude.js' + +const namespaceNames = { + GraphQLEnumType: `Enum`, + GraphQLInputObjectType: `InputObject`, + GraphQLInterfaceType: `Interface`, + GraphQLObjectType: `Object`, + GraphQLScalarType: `Scalar`, + GraphQLUnionType: `Union`, +} satisfies Record + +type AnyGraphQLFieldsType = GraphQLObjectType | GraphQLInterfaceType | GraphQLInputObjectType + +const definePointerRenderers = <$Renderers extends { [ClassName in keyof NameToClass]: any }>( + renderers: { + [ClassName in keyof $Renderers]: ( + config: Config, + node: ClassName extends keyof NameToClass ? InstanceType : never, + ) => string + }, +) => renderers + +const defineConcreteRenderers = < + $Renderers extends { [ClassName in keyof NameToClassNamedType]: any }, +>( + renderers: { + [ClassName in keyof $Renderers]: ( + config: Config, + node: ClassName extends keyof NameToClassNamedType ? InstanceType + : never, + ) => string + }, +): { + [ClassName in keyof $Renderers]: ( + node: ClassName extends keyof NameToClass ? InstanceType | null | undefined + : never, + ) => string +} => { + return Object.fromEntries( + Object.entries(renderers).map(([key, renderer]) => { + return [ + key, + (config: Config, node: any) => { + if (!node) return `` + return renderer(config, node) // eslint-disable-line + }, + ] + }), + ) as any +} + +const dispatchToPointerRenderer = (config: Config, node: AnyClass): string => { + // @ts-expect-error lookup + const renderer = pointerRenderers[node.constructor.name] // eslint-disable-line + if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) + return renderer(config, node) // eslint-disable-line +} + +const dispatchToConcreteRenderer = (config: Config, node: GraphQLNamedType): string => { + // @ts-expect-error lookup + const renderer = concreteRenderers[node.constructor.name] // eslint-disable-line + if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) + return renderer(config, node) // eslint-disable-line +} + +const pointerRenderers = definePointerRenderers({ + GraphQLNonNull: (config, node) => dispatchToPointerRenderer(config, node.ofType), + GraphQLEnumType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLEnumType, node.name), + GraphQLInputObjectType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLInputObjectType, node.name), + GraphQLInterfaceType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), + GraphQLList: (config, node) => Code.list(dispatchToPointerRenderer(config, node.ofType)), + GraphQLObjectType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), + GraphQLScalarType: (config, node) => `$.Scalars[${Code.quote(node.name)}]`, + GraphQLUnionType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), +}) + +const concreteRenderers = defineConcreteRenderers({ + GraphQLEnumType: (config, node) => + Code.TSDoc( + getDocumentation(config, node), + Code.export$( + Code.union( + node.name, + node.getValues().map((_) => Code.quote(_.name)), + ), + ), + ), + GraphQLInputObjectType: (config, node) => + Code.TSDoc( + getDocumentation(config, node), + Code.export$(Code.interface$( + node.name, + renderFields(config, node), + )), + ), + GraphQLInterfaceType: (config, node) => + Code.TSDoc(getDocumentation(config, node), Code.export$(Code.interface$(node.name, renderFields(config, node)))), + GraphQLObjectType: (config, node) => + Code.TSDoc(getDocumentation(config, node), Code.export$(Code.interface$(node.name, renderFields(config, node)))), + GraphQLScalarType: () => ``, + GraphQLUnionType: (config, node) => + Code.TSDoc( + getDocumentation(config, node), + Code.export$( + Code.union( + node.name, + node.getTypes().map((_) => dispatchToPointerRenderer(config, _) + `& { $$union:true}`), + ), + ), + ), +}) + +const getDocumentation = (config: Config, node: Describable) => { + const generalDescription = node.description + ?? (config.TSDoc.noDocPolicy === `message` ? defaultDescription(node) : null) + + const deprecationDescription = isDeprecatableNode(node) && node.deprecationReason + ? `@deprecated ${node.deprecationReason}` + : null + + const enumMemberDescriptions: string[] = isEnumType(node) + ? node.getValues() + .map(_ => { + // if (_.deprecationReason) + const deprecationDescription = _.deprecationReason ? `(DEPRECATED: ${_.deprecationReason})` : null + const generalDescription = _.description + ? _.description + : (config.TSDoc.noDocPolicy === `message` ? `Missing description.` : null) + if (!generalDescription && !deprecationDescription) return null + const content = [generalDescription, deprecationDescription].filter(_ => _ !== null).join(` `) + return [_, content] as const + }) + .filter((_): _ is [GraphQLEnumValue, string] => _ !== null) + .map(([node, description]) => { + const content = `"${node.name}" - ${description}` + return content + }) + : [] + const enumMemberDescription = enumMemberDescriptions.length > 0 + ? `Members\n${enumMemberDescriptions.join(`\n`)}` + : null + if (!enumMemberDescription && !generalDescription && !deprecationDescription) return null + const content = [generalDescription, enumMemberDescription, deprecationDescription].filter(_ => _ !== null) + .join(`\n\n`) + return content +} + +const defaultDescription = (node: Describable) => `There is no documentation for this ${getNodeDisplayName(node)}.` + +const renderFields = (config: Config, node: AnyGraphQLFieldsType): string => { + const __typenameField = node instanceof GraphQLObjectType ? [Code.field(`__typename`, `"${node.name}"`)] : [] + return Code.fields( + [ + ...__typenameField, + ...values(node.getFields()).map((field) => + Code.TSDoc(getDocumentation(config, field), Code.field(field.name, renderField(config, field))) + ), + ], + ) +} + +const renderField = (config: Config, field: AnyField): string => { + const [node, nullable] = field.type instanceof GraphQLNonNull ? [field.type.ofType, false] : [field.type, true] + return nullable ? Code.nullable(dispatchToPointerRenderer(config, node)) : dispatchToPointerRenderer(config, node) // eslint-disable-line +} + +const scalarTypeMap: Record = { + ID: `string`, + Int: `number`, + String: `string`, + Float: `number`, + Boolean: `boolean`, +} + +// high level + +interface Input { + schemaSource: string + options?: { + TSDoc?: { + noDocPolicy?: 'message' | 'ignore' + } + } +} + +interface Config { + TSDoc: { + noDocPolicy: 'message' | 'ignore' + } +} + +const resolveOptions = (options: Input['options']): Config => { + return { + TSDoc: { + noDocPolicy: options?.TSDoc?.noDocPolicy ?? `ignore`, + }, + } +} + +export const generateCode = (input: Input) => { + const schema = buildSchema(input.schemaSource) + const typeMapByKind = getTypeMapByKind(schema) + const config = resolveOptions(input.options) + + let code = `` + + const hasQuery = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Query`) + const hasMutation = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Mutation`) + const hasSubscription = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Subscription`) + + code += Code.export$(Code.namespace( + `$`, + Code.group( + Code.export$( + Code.interface$( + `Index`, + Code.fields([ + Code.field( + `Root`, + Code.object( + Code.fields([ + Code.field(`Query`, hasQuery ? `Root.Query` : `null`), + Code.field(`Mutation`, hasMutation ? `Root.Mutation` : `null`), + Code.field(`Subscription`, hasSubscription ? `Root.Subscription` : `null`), + ]), + ), + ), + Code.field( + `unions`, + Code.object( + Code.fields([ + Code.field( + `Union`, + typeMapByKind.GraphQLUnionType.length > 0 + ? Code.unionItems(typeMapByKind.GraphQLUnionType.map(_ => `Union.${_.name}`)) + : `null`, + ), + ]), + ), + ), + Code.field(`scalars`, `Scalars`), + ]), + ), + ), + Code.export$(Code.interface$( + `Scalars`, + ` + ${ + typeMapByKind.GraphQLScalarType.map((_) => { + // todo strict mode where instead of falling back to "any" we throw an error + const type = scalarTypeMap[_.name] || `string` + return Code.field(_.name, type) + }).join(`\n`) + } + `, + )), + ), + )) + + for (const [name, types] of entries(typeMapByKind)) { + if (name === `GraphQLScalarType`) continue + + const namespaceName = name === `GraphQLRootTypes` ? `Root` : namespaceNames[name] + code += Code.commentSectionTitle(namespaceName) + code += Code.export$(Code.namespace( + namespaceName, + types.length === 0 + ? `// -- no types --\n` + : types.map(_ => dispatchToConcreteRenderer(config, _)).join(`\n\n`), + )) + } + + return code +} + +export const generateFile = async (params: { schemaPath: string; typeScriptPath: string }) => { + const schemaSource = await fs.readFile(params.schemaPath, `utf8`) + const code = generateCode({ schemaSource }) + await fs.writeFile(params.typeScriptPath, code, { encoding: `utf8` }) +} diff --git a/src/lib/generateTypes.ts b/src/lib/generateTypes.ts deleted file mode 100644 index 62c318e60..000000000 --- a/src/lib/generateTypes.ts +++ /dev/null @@ -1,226 +0,0 @@ -// todo Emit JSDoc from GraphQL descriptions -import type { - GraphQLField, - GraphQLInputField, - GraphQLInputObjectType, - GraphQLInterfaceType, - GraphQLNamedType, -} from 'graphql' -import { GraphQLNonNull, GraphQLObjectType } from 'graphql' -import { buildSchema } from 'graphql' -import fs from 'node:fs/promises' -import { Code } from './Code.js' -import type { AnyClass, AnyNamedClassName, NameToClassNamedType } from './graphql.js' -import { getTypeMapByKind, type NameToClass } from './graphql.js' -import { entries, values } from './prelude.js' - -const namespaceNames = { - GraphQLEnumType: `Enum`, - GraphQLInputObjectType: `InputObject`, - GraphQLInterfaceType: `Interface`, - GraphQLObjectType: `Object`, - GraphQLScalarType: `Scalar`, - GraphQLUnionType: `Union`, -} satisfies Record - -type AnyGraphQLField = GraphQLField | GraphQLInputField -// type AnyGraphQLFieldMap = GraphQLFieldMap -// type AnyGraphQLNonNull = GraphQLNonNull -// type AnyGraphQLList = GraphQLList - -type AnyGraphQLFieldsType = GraphQLObjectType | GraphQLInterfaceType | GraphQLInputObjectType - -const definePointerRenderers = <$Renderers extends { [ClassName in keyof NameToClass]: any }>( - renderers: { - [ClassName in keyof $Renderers]: ( - node: ClassName extends keyof NameToClass ? InstanceType : never, - ) => string - }, -) => renderers - -const defineConcreteRenderers = < - $Renderers extends { [ClassName in keyof NameToClassNamedType]: any }, ->( - renderers: { - [ClassName in keyof $Renderers]: ( - node: ClassName extends keyof NameToClassNamedType ? InstanceType - : never, - ) => string - }, -): { - [ClassName in keyof $Renderers]: ( - node: ClassName extends keyof NameToClass ? InstanceType | null | undefined - : never, - ) => string -} => { - return Object.fromEntries( - Object.entries(renderers).map(([key, renderer]) => { - return [ - key, - (node: any) => { - if (!node) return `` - return renderer(node) // eslint-disable-line - }, - ] - }), - ) as any -} - -const dispatchToPointerRenderer = (node: AnyClass): string => { - // @ts-expect-error lookup - const renderer = pointerRenderers[node.constructor.name] // eslint-disable-line - if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) - return renderer(node) // eslint-disable-line -} - -const dispatchToConcreteRenderer = (node: GraphQLNamedType): string => { - // @ts-expect-error lookup - const renderer = concreteRenderers[node.constructor.name] // eslint-disable-line - if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) - return renderer(node) // eslint-disable-line -} - -const pointerRenderers = definePointerRenderers({ - GraphQLNonNull: (node) => dispatchToPointerRenderer(node.ofType), - GraphQLEnumType: (node) => Code.propertyAccess(namespaceNames.GraphQLEnumType, node.name), - GraphQLInputObjectType: (node) => Code.propertyAccess(namespaceNames.GraphQLInputObjectType, node.name), - GraphQLInterfaceType: (node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), - GraphQLList: (node) => Code.list(dispatchToPointerRenderer(node.ofType)), - GraphQLObjectType: (node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), - GraphQLScalarType: (node) => `$.Scalars[${Code.quote(node.name)}]`, - GraphQLUnionType: (node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), -}) - -const concreteRenderers = defineConcreteRenderers({ - GraphQLEnumType: (node) => - Code.export$( - Code.union( - node.name, - node.getValues().map((_) => Code.quote(_.name)), - ), - ), - GraphQLInputObjectType: (node) => - Code.export$(Code.interface$( - node.name, - renderFields(node), - )), - GraphQLInterfaceType: (node) => Code.export$(Code.interface$(node.name, renderFields(node))), - GraphQLObjectType: (node) => Code.export$(Code.interface$(node.name, renderFields(node))), - GraphQLScalarType: () => ``, - GraphQLUnionType: (node) => - Code.export$( - Code.union( - node.name, - node.getTypes().map((_) => dispatchToPointerRenderer(_) + `& { $$union:true}`), - ), - ), -}) - -const renderFields = (node: AnyGraphQLFieldsType): string => { - return Code.fields( - [ - ...(node instanceof GraphQLObjectType ? [Code.field(`__typename`, `"${node.name}"`)] : []), - ...values(node.getFields()).map((field) => Code.field(field.name, renderField(field))), - ], - ) -} - -const renderField = (field: AnyGraphQLField): string => { - const [fieldType, nullable] = field.type instanceof GraphQLNonNull ? [field.type.ofType, false] : [field.type, true] - return nullable ? Code.nullable(dispatchToPointerRenderer(fieldType)) : dispatchToPointerRenderer(fieldType) // eslint-disable-line -} - -const scalarTypeMap: Record = { - ID: `string`, - Int: `number`, - String: `string`, - Float: `number`, - Boolean: `boolean`, -} - -// high level - -interface Input { - schemaSource: string -} - -export const generateSchemaTypes = (input: Input) => { - const schema = buildSchema(input.schemaSource) - const typeMapByKind = getTypeMapByKind(schema) - - let code = `` - - const hasQuery = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Query`) - const hasMutation = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Mutation`) - const hasSubscription = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Subscription`) - - code += Code.export$(Code.namespace( - `$`, - Code.group( - Code.export$( - Code.interface$( - `Index`, - Code.fields([ - Code.field( - `Root`, - Code.object( - Code.fields([ - Code.field(`Query`, hasQuery ? `Root.Query` : `null`), - Code.field(`Mutation`, hasMutation ? `Root.Mutation` : `null`), - Code.field(`Subscription`, hasSubscription ? `Root.Subscription` : `null`), - ]), - ), - ), - Code.field( - `unions`, - Code.object( - Code.fields([ - Code.field( - `Union`, - typeMapByKind.GraphQLUnionType.length > 0 - ? Code.unionItems(typeMapByKind.GraphQLUnionType.map(_ => `Union.${_.name}`)) - : `null`, - ), - ]), - ), - ), - Code.field(`scalars`, `Scalars`), - ]), - ), - ), - Code.export$(Code.interface$( - `Scalars`, - ` - ${ - typeMapByKind.GraphQLScalarType.map((_) => { - // todo strict mode where instead of falling back to "any" we throw an error - const type = scalarTypeMap[_.name] || `string` - return Code.field(_.name, type) - }).join(`\n`) - } - `, - )), - ), - )) - - for (const [name, types] of entries(typeMapByKind)) { - if (name === `GraphQLScalarType`) continue - - const namespaceName = name === `GraphQLRootTypes` ? `Root` : namespaceNames[name] - code += Code.commentSectionTitle(namespaceName) - code += Code.export$(Code.namespace( - namespaceName, - types.length === 0 - ? `// -- no types --\n` - : types.map(dispatchToConcreteRenderer).join(`\n\n`), - )) - } - - return code -} - -export const generateFile = async (params: { schemaPath: string; typeScriptPath: string }) => { - const schemaSource = await fs.readFile(params.schemaPath, `utf8`) - const code = generateSchemaTypes({ schemaSource }) - await fs.writeFile(params.typeScriptPath, code, { encoding: `utf8` }) -} diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index 459d2f27b..01d87ccf0 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -1,4 +1,4 @@ -import type { GraphQLSchema } from 'graphql' +import type { EnumValueNode, GraphQLEnumValue, GraphQLField, GraphQLInputField, GraphQLSchema } from 'graphql' import { GraphQLEnumType, GraphQLInputObjectType, @@ -87,8 +87,71 @@ export const NameToClass = { ...NamedNameToClass, } as const +export type AnyGraphQLOutputField = GraphQLField + +export type AnyField = AnyGraphQLOutputField | GraphQLInputField + export type NameToClass = typeof NameToClass +export type NodeName = keyof NameToClass + +export type NodeNamePlus = NodeName | 'GraphQLField' + export type AnyNamedClassName = keyof NamedNameToClass export type AnyClass = InstanceType + +/** + * Groups + */ + +export type Describable = + | GraphQLUnionType + | GraphQLObjectType + | GraphQLInputObjectType + | AnyField + | GraphQLInterfaceType + | GraphQLEnumType + +export const getNodeName = (node: Describable): NodeNamePlus => { + switch (true) { + case node instanceof GraphQLObjectType: + return `GraphQLObjectType` + case node instanceof GraphQLInputObjectType: + return `GraphQLInputObjectType` + case node instanceof GraphQLUnionType: + return `GraphQLUnionType` + case node instanceof GraphQLInterfaceType: + return `GraphQLInterfaceType` + case node instanceof GraphQLEnumType: + return `GraphQLEnumType` + case node instanceof GraphQLScalarType: + return `GraphQLScalarType` + default: + return `GraphQLField` + throw new Error(`Unknown node type: ${node.name}`) + } +} + +// const displayNames = { +// GraphQLEnumType: `Enum`, +// GraphQLInputObjectType: `InputObject`, +// GraphQLInterfaceType: `Interface`, +// GraphQLList: `List`, +// GraphQLNonNull: `NonNull`, +// GraphQLObjectType: `Object`, +// GraphQLScalarType: `Scalar`, +// GraphQLUnionType: `Union`, +// } satisfies Record + +export const getNodeDisplayName = (node: Describable) => { + return toDisplayName(getNodeName(node)) +} + +const toDisplayName = (nodeName: NodeNamePlus) => { + return nodeName.replace(/^GraphQL/, ``).replace(/Type$/, ``) +} + +export const isDeprecatableNode = (node: object): node is GraphQLEnumValue | AnyField => { + return `deprecationReason` in node +} diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index a18a6f070..c3f408d5d 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -2,12 +2,22 @@ type Query { string: String scalars: Scalars fooBarUnion: FooBarUnion + abcEnum: ABCEnum } +""" +Union documentation. +""" union FooBarUnion = Foo | Bar +""" +Object documentation. +""" type Foo { - a: String + """ + Field documentation. + """ + a: String @deprecated(reason: "Field a is deprecated.") } type Bar { @@ -21,3 +31,18 @@ type Scalars { d: Boolean e: ID } + +""" +Enum documentation. +""" +enum ABCEnum { + A @deprecated(reason: "Enum value A is deprecated.") + """ + Enum B member documentation. + """ + B + """ + Enum C member documentation. + """ + C @deprecated(reason: "Enum value C is deprecated.") +} diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 516ed6141..348b9895d 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -31,6 +31,7 @@ __typename: "Query" string: $.Scalars["String"] | null scalars: Object.Scalars | null fooBarUnion: Union.FooBarUnion | null +abcEnum: Enum.ABCEnum | null } } @@ -39,8 +40,18 @@ fooBarUnion: Union.FooBarUnion | null // ------------------------------------------------------------ // export namespace Enum { -// -- no types -- - +/** +* Enum documentation. +* +* Members +* "A" - (DEPRECATED: Enum value A is deprecated.) +* "B" - Enum B member documentation. +* "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) +*/ +export type ABCEnum = +| "A" +| "B" +| "C" } // ------------------------------------------------------------ // @@ -66,8 +77,16 @@ export namespace Interface { // ------------------------------------------------------------ // export namespace Object { +/** +* Object documentation. +*/ export interface Foo { __typename: "Foo" +/** +* Field documentation. +* +* @deprecated Field a is deprecated. +*/ a: $.Scalars["String"] | null } @@ -91,6 +110,9 @@ e: $.Scalars["ID"] | null // ------------------------------------------------------------ // export namespace Union { +/** +* Union documentation. +*/ export type FooBarUnion = | Object.Foo& { $$union:true} | Object.Bar& { $$union:true} diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index 6973635a1..d28af4082 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -34,6 +34,7 @@ __typename: "Query" string: $.Scalars["String"] | null scalars: Object.Scalars | null fooBarUnion: Union.FooBarUnion | null +abcEnum: Enum.ABCEnum | null } } @@ -42,8 +43,18 @@ fooBarUnion: Union.FooBarUnion | null // ------------------------------------------------------------ // export namespace Enum { -// -- no types -- - +/** +* Enum documentation. +* +* Members +* "A" - (DEPRECATED: Enum value A is deprecated.) +* "B" - Enum B member documentation. +* "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) +*/ +export type ABCEnum = +| "A" +| "B" +| "C" } // ------------------------------------------------------------ // @@ -69,8 +80,16 @@ export namespace Interface { // ------------------------------------------------------------ // export namespace Object { +/** +* Object documentation. +*/ export interface Foo { __typename: "Foo" +/** +* Field documentation. +* +* @deprecated Field a is deprecated. +*/ a: $.Scalars["String"] | null } @@ -94,6 +113,9 @@ e: $.Scalars["ID"] | null // ------------------------------------------------------------ // export namespace Union { +/** +* Union documentation. +*/ export type FooBarUnion = | Object.Foo& { $$union:true} | Object.Bar& { $$union:true} diff --git a/tests/builder/generate.test.ts b/tests/builder/generate.test.ts index 203a780f7..36a8d714e 100644 --- a/tests/builder/generate.test.ts +++ b/tests/builder/generate.test.ts @@ -1,6 +1,6 @@ import { readFile } from 'fs/promises' import { expect, test } from 'vitest' -import { generateFile } from '../../src/lib/generateTypes.js' +import { generateFile } from '../../src/lib/generate.js' test(`generates types from GraphQL SDL file`, async () => { await generateFile({ From 06e42e665243fcf850773a3311c32c606f92361b Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 18 Feb 2024 14:20:16 -0500 Subject: [PATCH 13/71] test enum --- src/lib/generate.ts | 5 ++--- tests/builder/_/schema.graphql | 3 +++ tests/builder/_/schema.ts | 3 +++ tests/builder/__snapshots__/generate.test.ts.snap | 3 +++ tests/builder/types/selectionSet.test-d.ts | 8 ++++++++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/lib/generate.ts b/src/lib/generate.ts index 8be49887e..371e66155 100644 --- a/src/lib/generate.ts +++ b/src/lib/generate.ts @@ -130,7 +130,6 @@ const getDocumentation = (config: Config, node: Describable) => { const enumMemberDescriptions: string[] = isEnumType(node) ? node.getValues() .map(_ => { - // if (_.deprecationReason) const deprecationDescription = _.deprecationReason ? `(DEPRECATED: ${_.deprecationReason})` : null const generalDescription = _.description ? _.description @@ -211,12 +210,12 @@ export const generateCode = (input: Input) => { const typeMapByKind = getTypeMapByKind(schema) const config = resolveOptions(input.options) - let code = `` - const hasQuery = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Query`) const hasMutation = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Mutation`) const hasSubscription = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Subscription`) + let code = `` + code += Code.export$(Code.namespace( `$`, Code.group( diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index c3f408d5d..01c270ed9 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -2,6 +2,9 @@ type Query { string: String scalars: Scalars fooBarUnion: FooBarUnion + """ + Query enum field documentation. + """ abcEnum: ABCEnum } diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 348b9895d..f82a207cb 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -31,6 +31,9 @@ __typename: "Query" string: $.Scalars["String"] | null scalars: Object.Scalars | null fooBarUnion: Union.FooBarUnion | null +/** +* Query enum field documentation. +*/ abcEnum: Enum.ABCEnum | null } } diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index d28af4082..8a156a31f 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -34,6 +34,9 @@ __typename: "Query" string: $.Scalars["String"] | null scalars: Object.Scalars | null fooBarUnion: Union.FooBarUnion | null +/** +* Query enum field documentation. +*/ abcEnum: Enum.ABCEnum | null } } diff --git a/tests/builder/types/selectionSet.test-d.ts b/tests/builder/types/selectionSet.test-d.ts index a7276b97a..2936c1aa7 100644 --- a/tests/builder/types/selectionSet.test-d.ts +++ b/tests/builder/types/selectionSet.test-d.ts @@ -5,13 +5,18 @@ import type * as Schema from '../_/schema.js' type S = SelectionSet test(`general`, () => { + // scalar assertType({ string: true }) assertType({ string: false }) assertType({ string: 1 }) assertType({ string: 0 }) assertType({ string: undefined }) + + // object type assertType({ __typename: true }) assertType({ scalars: { a: true } }) + + // union type assertType({ fooBarUnion: { __typename: true } }) assertType({ fooBarUnion: { onFoo: { __typename: true } } }) assertType({ fooBarUnion: { onFoo: { a: true } } }) @@ -21,4 +26,7 @@ test(`general`, () => { assertType({ fooBarUnion: { onBar: { b: true } } }) // @ts-expect-error no a assertType({ fooBarUnion: { onBar: { a: true } } }) + + // enum type + assertType({ abcEnum: true }) }) From 7580e33a0d1ee58a61d234b9c00309ba248aab24 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 18 Feb 2024 14:42:17 -0500 Subject: [PATCH 14/71] todo tests --- tests/builder/types/selectionSet.test-d.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/builder/types/selectionSet.test-d.ts b/tests/builder/types/selectionSet.test-d.ts index 2936c1aa7..540f3aade 100644 --- a/tests/builder/types/selectionSet.test-d.ts +++ b/tests/builder/types/selectionSet.test-d.ts @@ -1,3 +1,4 @@ +import { skip } from 'node:test' import { assertType, test } from 'vitest' import type { SelectionSet } from '../../../src/lib/SelectionSet.js' import type * as Schema from '../_/schema.js' @@ -13,6 +14,10 @@ test(`general`, () => { assertType({ string: undefined }) // object type + // @ts-expect-error excess property check + assertType({ string2: true }) + // @ts-expect-error excess property check + assertType({ scalars: { a2: true } }) assertType({ __typename: true }) assertType({ scalars: { a: true } }) @@ -29,4 +34,19 @@ test(`general`, () => { // enum type assertType({ abcEnum: true }) + + // todo alias + // alias: enum + assertType({ abcEnum_as_enum: true }) + // alias: object + assertType({ scalars_as_s: { a: true } }) + + // todo directive @skip + assertType({ string: skip({ if: true }) }) + + // todo arguments + assertType({ foo: args({ x: 1 }) }) + + // todo arguments + directive + assertType({ foo: args({ x: 1 }).skip({ if: true }) }) }) From d81c564403fdb09c82e2252c55a360179a503d22 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 18 Feb 2024 14:43:38 -0500 Subject: [PATCH 15/71] todo --- package.json | 1 + pnpm-lock.yaml | 7 +++++++ src/lib/generate.ts | 1 + 3 files changed, 9 insertions(+) diff --git a/package.json b/package.json index c186c9ba5..118148ae7 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "release:pr": "dripip pr" }, "dependencies": { + "@dprint/formatter": "^0.2.1", "@graphql-typed-document-node/core": "^3.2.0", "dprint": "^0.45.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ac0c2349..c31e06f2c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@dprint/formatter': + specifier: ^0.2.1 + version: 0.2.1 '@graphql-typed-document-node/core': specifier: ^3.2.0 version: 3.2.0(graphql@16.8.1) @@ -262,6 +265,10 @@ packages: dev: false optional: true + /@dprint/formatter@0.2.1: + resolution: {integrity: sha512-GCzgRt2o4mhZLy8L47k2A+q9EMG/jWhzZebE29EqKsxmjDrSfv2VisEj/Q+39OOf04jTkEfB/TRO+IZSyxHdYg==} + dev: false + /@dprint/linux-arm64-glibc@0.45.0: resolution: {integrity: sha512-NgIpvZHpiQaY4DxSygxknxBtvKE2KLK9dEbUNKNE098yTHhGq7ouPsoM7RtsO34RHJ3tEZLLJEuBHn20XP8LMg==} cpu: [arm64] diff --git a/src/lib/generate.ts b/src/lib/generate.ts index 371e66155..12643b366 100644 --- a/src/lib/generate.ts +++ b/src/lib/generate.ts @@ -282,6 +282,7 @@ export const generateCode = (input: Input) => { } export const generateFile = async (params: { schemaPath: string; typeScriptPath: string }) => { + // todo use @dprint/formatter const schemaSource = await fs.readFile(params.schemaPath, `utf8`) const code = generateCode({ schemaSource }) await fs.writeFile(params.typeScriptPath, code, { encoding: `utf8` }) From 1dc19c3b266592b0aa00ba83d6c2a4ec8ffc2dd8 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 18 Feb 2024 21:03:14 -0500 Subject: [PATCH 16/71] alias support --- src/lib/SelectionSet.ts | 19 +++++-- src/lib/prelude.ts | 62 ++++++++++++++++++++++ tests/builder/types/selectionSet.test-d.ts | 7 ++- 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/lib/SelectionSet.ts b/src/lib/SelectionSet.ts index 1c56f5cba..801f5698a 100644 --- a/src/lib/SelectionSet.ts +++ b/src/lib/SelectionSet.ts @@ -1,3 +1,4 @@ +import type { NonEmptyString } from './prelude.js' import type { ExcludeNull, Indicator, @@ -18,10 +19,20 @@ export type SelectionSet = TSError<'SelectionSet', 'T is not a SelectableFieldsType',{ T:T }> // todo an empty selection set should be a static type error -// dprint-ignore -type SelectionSetObject<$ObjectType extends ObjectType, $Index extends SchemaIndex> = { - [Key in keyof $ObjectType]?: SelectionSet, $Index> -} +type SelectionSetObject<$Object extends ObjectType, $Index extends SchemaIndex> = + & { + [Key in keyof $Object]?: SelectionSet, $Index> + } + /** + * Alias support. + * Allow every field to also be given as a key with this pattern `_as_: ...` + */ + & { + [Key in keyof $Object as `${keyof $Object & string}_as_${NonEmptyString}`]?: SelectionSet< + ExcludeNull<$Object[Key]>, + $Index + > + } // TODO why does $object no get passed to this in a distributed way? type SelectionSetUnion<$Object extends ObjectType, $Index extends SchemaIndex> = diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index d37381134..b0711ae6b 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -87,3 +87,65 @@ export const isPlainObject = (value: unknown): value is object => { export const entries = >(obj: T) => Object.entries(obj) as [keyof T, T[keyof T]][] export const values = >(obj: T): T[keyof T][] => Object.values(obj) as T[keyof T][] + +export type Exact = + | (A extends unknown ? (W extends A ? { [K in keyof A]: Exact } : W) : never) + | (A extends Narrowable ? A : never) + +export type Narrowable = string | number | bigint | boolean | [] + +export type Character = + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | 'g' + | 'h' + | 'i' + | 'j' + | 'k' + | 'l' + | 'm' + | 'n' + | 'o' + | 'p' + | 'q' + | 'r' + | 's' + | 't' + | 'u' + | 'v' + | 'w' + | 'x' + | 'y' + | 'z' + | 'A' + | 'B' + | 'C' + | 'D' + | 'E' + | 'F' + | 'G' + | 'H' + | 'I' + | 'J' + | 'K' + | 'L' + | 'M' + | 'N' + | 'O' + | 'P' + | 'Q' + | 'R' + | 'S' + | 'T' + | 'U' + | 'V' + | 'W' + | 'X' + | 'Y' + | 'Z' + +export type NonEmptyString = `${Character}${string}` diff --git a/tests/builder/types/selectionSet.test-d.ts b/tests/builder/types/selectionSet.test-d.ts index 540f3aade..b7bf65a35 100644 --- a/tests/builder/types/selectionSet.test-d.ts +++ b/tests/builder/types/selectionSet.test-d.ts @@ -35,11 +35,16 @@ test(`general`, () => { // enum type assertType({ abcEnum: true }) - // todo alias // alias: enum assertType({ abcEnum_as_enum: true }) // alias: object assertType({ scalars_as_s: { a: true } }) + // @ts-expect-error invalid alias key format + assertType({ scalars_as_: { a: true } }) + // @ts-expect-error invalid alias key format + assertType({ scalars_as: { a: true } }) + // @ts-expect-error invalid alias key format + assertType({ scalars2_as_s: { a: true } }) // todo directive @skip assertType({ string: skip({ if: true }) }) From c66ddc53a31e7a5fd3889038b475c0f9abe1de19 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Mon, 19 Feb 2024 21:56:20 -0500 Subject: [PATCH 17/71] args and more --- src/lib/Code.ts | 7 +- src/lib/SelectionSet.ts | 39 ++++-- src/lib/generate.ts | 60 +++++++-- src/lib/graphql.ts | 4 + src/lib/prelude.ts | 4 + src/lib/schemaTypes.ts | 40 +++++- tests/builder/_/schema.graphql | 7 +- tests/builder/_/schema.ts | 29 ++++- .../__snapshots__/generate.test.ts.snap | 29 ++++- tests/builder/types/selectionSet.test-d.ts | 116 ++++++++++++++++-- 10 files changed, 291 insertions(+), 44 deletions(-) diff --git a/src/lib/Code.ts b/src/lib/Code.ts index f37ea9dce..261f166e4 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -5,8 +5,13 @@ export namespace Code { export const union = (name: string, types: string[]) => `type ${name} =\n| ${Code.unionItems(types)}` export const unionItems = (types: string[]) => types.join(`\n| `) export const list = (type: string) => `Array<${type}>` - export const field = (name: string, type: string) => `${name}: ${type}` + export const field = (name: string, type: string, options?: { optional?: boolean }) => { + if (options?.optional) return `${name}?: ${type}` + return `${name}: ${type}` + } + export const optionalField = (name: string, type: string) => Code.field(name, type, { optional: true }) export const fields = (fieldTypes: string[]) => fieldTypes.join(`\n`) + export const intersection = (a: string, b: string) => `${a} & ${b}` export const object = (fields: string) => `{\n${fields}\n}` export const interface$ = (name: string, fields: string) => `interface ${name} ${Code.object(fields)}` export const export$ = (thing: string) => `export ${thing}` diff --git a/src/lib/SelectionSet.ts b/src/lib/SelectionSet.ts index 801f5698a..e2887a51d 100644 --- a/src/lib/SelectionSet.ts +++ b/src/lib/SelectionSet.ts @@ -1,7 +1,10 @@ -import type { NonEmptyString } from './prelude.js' +import type { MaybeList, NonEmptyString } from './prelude.js' import type { + ClientIndicator, ExcludeNull, + FieldDirectives, Indicator, + NoArgsIndicator, ObjectType, OmitUnionBrand, ScalarType, @@ -10,31 +13,45 @@ import type { } from './schemaTypes.js' import type { TSError } from './TSError.js' -// todo if is a union type then must have each union member become an on_... field +type OmitArgs = Omit + // dprint-ignore export type SelectionSet = - T extends ScalarType ? Indicator : - ExcludeNull extends UnionType ? SelectionSetUnion, $Index> & { __typename?: Indicator } : - T extends ObjectType ? SelectionSetObject : - TSError<'SelectionSet', 'T is not a SelectableFieldsType',{ T:T }> + T extends ScalarType ? Indicator : + ExcludeNull extends UnionType ? SelectionSetUnion, $Index> & { __typename?: NoArgsIndicator } : + T extends ObjectType ? SelectionSetObject, $Index> & Arguments : + TSError<'SelectionSet', 'T is not a SelectableFieldsType', { T:T }> -// todo an empty selection set should be a static type error type SelectionSetObject<$Object extends ObjectType, $Index extends SchemaIndex> = & { - [Key in keyof $Object]?: SelectionSet, $Index> + [Key in keyof OmitArgs<$Object>]?: SelectionSet, $Index> } /** * Alias support. * Allow every field to also be given as a key with this pattern `_as_: ...` */ & { - [Key in keyof $Object as `${keyof $Object & string}_as_${NonEmptyString}`]?: SelectionSet< + [Key in keyof OmitArgs<$Object> as `${keyof OmitArgs<$Object> & string}_as_${NonEmptyString}`]?: SelectionSet< ExcludeNull<$Object[Key]>, $Index > } + & FieldDirectives + /** + * Inline fragments for field groups. + * @see https://spec.graphql.org/draft/#sec-Inline-Fragments + */ + & { + ___?: MaybeList> + } + /** + * Special property to select all scalars. + */ + & { $scalars?: ClientIndicator } + +type Arguments<$FieldType> = '$' extends keyof $FieldType ? Pick<$FieldType, '$'> : {} -// TODO why does $object no get passed to this in a distributed way? +// TODO why does $object not get passed to this in a distributed way? type SelectionSetUnion<$Object extends ObjectType, $Index extends SchemaIndex> = & { [Key in $Object['__typename'] as `on${Capitalize}`]?: SelectionSet< @@ -42,4 +59,4 @@ type SelectionSetUnion<$Object extends ObjectType, $Index extends SchemaIndex> = $Index > } - & { __typename?: Indicator } + & { __typename?: NoArgsIndicator } diff --git a/src/lib/generate.ts b/src/lib/generate.ts index 12643b366..72c40b2dc 100644 --- a/src/lib/generate.ts +++ b/src/lib/generate.ts @@ -1,12 +1,24 @@ // todo Emit JSDoc from GraphQL descriptions -import type { GraphQLEnumValue, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLNamedType } from 'graphql' +import type { + GraphQLArgument, + GraphQLEnumValue, + GraphQLInputObjectType, + GraphQLInterfaceType, + GraphQLNamedType, +} from 'graphql' import { GraphQLNonNull, GraphQLObjectType, isEnumType } from 'graphql' import { buildSchema } from 'graphql' import _ from 'json-bigint' import fs from 'node:fs/promises' import { Code } from './Code.js' import type { AnyClass, AnyField, AnyNamedClassName, Describable, NameToClassNamedType } from './graphql.js' -import { getNodeDisplayName, getTypeMapByKind, isDeprecatableNode, type NameToClass } from './graphql.js' +import { + getNodeDisplayName, + getTypeMapByKind, + isDeprecatableNode, + isGraphQLOutputField, + type NameToClass, +} from './graphql.js' import { entries, values } from './prelude.js' const namespaceNames = { @@ -74,13 +86,13 @@ const dispatchToConcreteRenderer = (config: Config, node: GraphQLNamedType): str const pointerRenderers = definePointerRenderers({ GraphQLNonNull: (config, node) => dispatchToPointerRenderer(config, node.ofType), - GraphQLEnumType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLEnumType, node.name), - GraphQLInputObjectType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLInputObjectType, node.name), - GraphQLInterfaceType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), + GraphQLEnumType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLEnumType, node.name), + GraphQLInputObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInputObjectType, node.name), + GraphQLInterfaceType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), GraphQLList: (config, node) => Code.list(dispatchToPointerRenderer(config, node.ofType)), - GraphQLObjectType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), - GraphQLScalarType: (config, node) => `$.Scalars[${Code.quote(node.name)}]`, - GraphQLUnionType: (config, node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), + GraphQLObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), + GraphQLScalarType: (_, node) => `$.Scalars[${Code.quote(node.name)}]`, + GraphQLUnionType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), }) const concreteRenderers = defineConcreteRenderers({ @@ -168,8 +180,36 @@ const renderFields = (config: Config, node: AnyGraphQLFieldsType): string => { } const renderField = (config: Config, field: AnyField): string => { - const [node, nullable] = field.type instanceof GraphQLNonNull ? [field.type.ofType, false] : [field.type, true] - return nullable ? Code.nullable(dispatchToPointerRenderer(config, node)) : dispatchToPointerRenderer(config, node) // eslint-disable-line + const { node, nullable } = unwrapNonNull(field.type) + + const fieldBase = dispatchToPointerRenderer(config, node) + + const args = isGraphQLOutputField(field) && field.args.length > 0 ? renderArgs(config, field.args) : null + const fieldWithArgs = args ? Code.intersection(fieldBase, args) : fieldBase + + const fieldWithArgsWithNullable = nullable ? Code.nullable(fieldWithArgs) : fieldWithArgs + + return fieldWithArgsWithNullable +} + +const renderArgs = (config: Config, args: readonly GraphQLArgument[]) => { + let hasRequiredArgs = false + const argsRendered = Code.object(Code.fields(args.map(arg => { + const { node, nullable } = unwrapNonNull(arg.type) + hasRequiredArgs = hasRequiredArgs || !nullable + return Code.field( + arg.name, + nullable ? Code.nullable(dispatchToPointerRenderer(config, node)) : dispatchToPointerRenderer(config, node), + { optional: nullable }, + ) + }))) + const allArgsOptional = !hasRequiredArgs + return Code.object(Code.field(`$`, argsRendered, { optional: allArgsOptional })) +} + +const unwrapNonNull = (node: AnyClass): { node: AnyClass; nullable: boolean } => { + const [nodeUnwrapped, nullable] = node instanceof GraphQLNonNull ? [node.ofType, false] : [node, true] + return { node: nodeUnwrapped, nullable } } const scalarTypeMap: Record = { diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index 01d87ccf0..af0ae6d9b 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -101,6 +101,10 @@ export type AnyNamedClassName = keyof NamedNameToClass export type AnyClass = InstanceType +export const isGraphQLOutputField = (object: object): object is AnyGraphQLOutputField => { + return `args` in object +} + /** * Groups */ diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index b0711ae6b..13738badf 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -149,3 +149,7 @@ export type Character = | 'Z' export type NonEmptyString = `${Character}${string}` + +export type MaybeList = T | T[] + +export type NotEmptyObject = keyof T extends never ? never : T diff --git a/src/lib/schemaTypes.ts b/src/lib/schemaTypes.ts index 6d6183a1d..8cae5b8ee 100644 --- a/src/lib/schemaTypes.ts +++ b/src/lib/schemaTypes.ts @@ -19,4 +19,42 @@ export type UnionType = ObjectType & { $$union: true } export type ExcludeNull = Exclude export type OmitUnionBrand = Omit -export type Indicator = boolean | 1 | 0 +/** + * Should this field be selected? + */ +export type ClientIndicator = boolean | 1 | 0 + +/** + * Field selection in general, with directives support too. + * If a field directive is given as an indicator then it implies "select this" e.g. `true`/`1`. + * Of course the semantics of the directive may change the derived type (e.g. `skip` means the field might not show up in the result set) + */ +export type NoArgsIndicator = ClientIndicator | FieldDirectives + +// dprint-ignore +export type Indicator = +'$' extends keyof Type ? undefined extends Type['$'] ? (Pick & FieldDirectives) | ClientIndicator + : (Pick & FieldDirectives) + : NoArgsIndicator + +/** + * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives + */ +export interface FieldDirectives { + /** + * https://spec.graphql.org/draft/#sec--skip + */ + $skip?: boolean | { if?: boolean } + /** + * https://spec.graphql.org/draft/#sec--include + */ + $include?: boolean | { if?: boolean } + /** + * @see https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#defer + */ + $defer?: boolean | { if?: boolean; label?: string } + /** + * @see https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#stream + */ + $stream?: boolean | { if?: boolean; label?: string; initialCount?: number } +} diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index 01c270ed9..51cf5cd83 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -1,6 +1,9 @@ type Query { string: String - scalars: Scalars + stringWithRequiredArg(string:String!): String + stringWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): String + object: Object + objectWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): Object fooBarUnion: FooBarUnion """ Query enum field documentation. @@ -27,7 +30,7 @@ type Bar { b: Int } -type Scalars { +type Object { a: String b: Int c: Float diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index f82a207cb..8406873a3 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -29,7 +29,30 @@ export namespace Root { export interface Query { __typename: "Query" string: $.Scalars["String"] | null -scalars: Object.Scalars | null +stringWithRequiredArg: $.Scalars["String"] & { +$: { +string: $.Scalars["String"] +} +} | null +stringWithArgs: $.Scalars["String"] & { +$?: { +string?: $.Scalars["String"] | null +int?: $.Scalars["Int"] | null +float?: $.Scalars["Float"] | null +boolean?: $.Scalars["Boolean"] | null +id?: $.Scalars["ID"] | null +} +} | null +object: Object.Object | null +objectWithArgs: Object.Object & { +$?: { +string?: $.Scalars["String"] | null +int?: $.Scalars["Int"] | null +float?: $.Scalars["Float"] | null +boolean?: $.Scalars["Boolean"] | null +id?: $.Scalars["ID"] | null +} +} | null fooBarUnion: Union.FooBarUnion | null /** * Query enum field documentation. @@ -98,8 +121,8 @@ __typename: "Bar" b: $.Scalars["Int"] | null } -export interface Scalars { -__typename: "Scalars" +export interface Object { +__typename: "Object" a: $.Scalars["String"] | null b: $.Scalars["Int"] | null c: $.Scalars["Float"] | null diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index 8a156a31f..aa82150e1 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -32,7 +32,30 @@ export namespace Root { export interface Query { __typename: "Query" string: $.Scalars["String"] | null -scalars: Object.Scalars | null +stringWithRequiredArg: $.Scalars["String"] & { +$: { +string: $.Scalars["String"] +} +} | null +stringWithArgs: $.Scalars["String"] & { +$?: { +string?: $.Scalars["String"] | null +int?: $.Scalars["Int"] | null +float?: $.Scalars["Float"] | null +boolean?: $.Scalars["Boolean"] | null +id?: $.Scalars["ID"] | null +} +} | null +object: Object.Object | null +objectWithArgs: Object.Object & { +$?: { +string?: $.Scalars["String"] | null +int?: $.Scalars["Int"] | null +float?: $.Scalars["Float"] | null +boolean?: $.Scalars["Boolean"] | null +id?: $.Scalars["ID"] | null +} +} | null fooBarUnion: Union.FooBarUnion | null /** * Query enum field documentation. @@ -101,8 +124,8 @@ __typename: "Bar" b: $.Scalars["Int"] | null } -export interface Scalars { -__typename: "Scalars" +export interface Object { +__typename: "Object" a: $.Scalars["String"] | null b: $.Scalars["Int"] | null c: $.Scalars["Float"] | null diff --git a/tests/builder/types/selectionSet.test-d.ts b/tests/builder/types/selectionSet.test-d.ts index b7bf65a35..3dba7ce18 100644 --- a/tests/builder/types/selectionSet.test-d.ts +++ b/tests/builder/types/selectionSet.test-d.ts @@ -1,4 +1,3 @@ -import { skip } from 'node:test' import { assertType, test } from 'vitest' import type { SelectionSet } from '../../../src/lib/SelectionSet.js' import type * as Schema from '../_/schema.js' @@ -17,9 +16,9 @@ test(`general`, () => { // @ts-expect-error excess property check assertType({ string2: true }) // @ts-expect-error excess property check - assertType({ scalars: { a2: true } }) + assertType({ object: { a2: true } }) assertType({ __typename: true }) - assertType({ scalars: { a: true } }) + assertType({ object: { a: true } }) // union type assertType({ fooBarUnion: { __typename: true } }) @@ -38,20 +37,111 @@ test(`general`, () => { // alias: enum assertType({ abcEnum_as_enum: true }) // alias: object - assertType({ scalars_as_s: { a: true } }) + assertType({ object_as_o: { a: true } }) // @ts-expect-error invalid alias key format - assertType({ scalars_as_: { a: true } }) + assertType({ object_as_: { a: true } }) // @ts-expect-error invalid alias key format - assertType({ scalars_as: { a: true } }) + assertType({ object_as: { a: true } }) // @ts-expect-error invalid alias key format - assertType({ scalars2_as_s: { a: true } }) + assertType({ object2_as_o: { a: true } }) - // todo directive @skip - assertType({ string: skip({ if: true }) }) + // directives + // @skip + // on scalar + assertType({ string: { $skip: true } }) + assertType({ string: { $skip: false } }) + assertType({ string: { $skip: { if: true } } }) + assertType({ string: { $skip: { if: false } } }) + assertType({ string: { $skip: {} } }) + assertType({ string: { $skip: {} } }) + // assertType({ string: skip() }) + // on object + assertType({ object: { $skip: true, a: true } }) + // assertType({ scalars: skip().select({ a: true }) }) + // on fragment + assertType({ fooBarUnion: { onBar: { $skip: true, b: true } } }) + // @include + assertType({ string: { $include: true } }) + assertType({ string: { $include: false } }) + assertType({ string: { $include: { if: true } } }) + assertType({ string: { $include: { if: false } } }) + assertType({ string: { $include: {} } }) + assertType({ string: { $include: {} } }) + // assertType({ string: include() }) - // todo arguments - assertType({ foo: args({ x: 1 }) }) + // @defer + assertType({ string: { $defer: true } }) + assertType({ string: { $defer: { if: true, label: `foo` } } }) + assertType({ string: { $defer: { if: true } } }) + assertType({ string: { $defer: {} } }) - // todo arguments + directive - assertType({ foo: args({ x: 1 }).skip({ if: true }) }) + // (todo limit to lists?) + // @stream + assertType({ string: { $stream: true } }) + assertType({ string: { $stream: { if: true, label: `foo`, initialCount: 0 } } }) + assertType({ string: { $stream: { if: true, label: `foo` } } }) + assertType({ string: { $stream: { if: true } } }) + assertType({ string: { $stream: {} } }) + + // group of fields + assertType({ object: { ___: { $skip: true, a: true, b: true } } }) + assertType({ object: { ___: [{ $skip: true, a: true, b: true }] } }) + + // Arguments + // all-optional on object + assertType({ objectWithArgs: { $: {}, a: true } }) + assertType({ objectWithArgs: { a: true } }) + assertType({ + objectWithArgs: { + $: { + boolean: true, + float: 1, + id: `id`, + int: 3, + string: `abc`, + }, + a: true, + }, + }) + // builder interface + // assertType({ foo: args({ ... }) }) + // all-optional on scalar + assertType({ stringWithArgs: true }) + assertType({ stringWithArgs: {} }) + assertType({ + stringWithArgs: { + $: { + boolean: true, + float: 1, + id: `id`, + int: 3, + string: `abc`, + }, + }, + }) + // all-optional + scalar + directive + assertType({ stringWithArgs: { $: { boolean: true }, $skip: true } }) + // builder interface + // assertType({ foo: args({ boolean: true }).skip().select({ x: 1 }) }) + // 1+ required + scalar + assertType({ stringWithRequiredArg: { $: { string: `` } } }) + // @ts-expect-error missing "string" arg + assertType({ stringWithRequiredArg: { $: {} } }) + // @ts-expect-error missing args ("$") + assertType({ stringWithRequiredArg: {} }) + + // all-scalars "client directive" + // object + assertType({ object: { $scalars: true } }) + // @ts-expect-error no directives on scalars field + assertType({ scalars: { $scalars: { $skip: true } } }) + // union fragment + assertType({ fooBarUnion: { onBar: { $scalars: true } } }) + // assertType({ scalars: select() }) + + // todo empty selection set not allowed, with arguments given + // todo empty selection set not allowed, with directive given + // todo empty selection set not allowed + // // @ts-expect-error empty selection set not allowed + // assertType({ scalars: {} }) }) From a8f039d1f549200902593d33b0842c8fd367212a Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Tue, 20 Feb 2024 23:26:34 -0500 Subject: [PATCH 18/71] work --- src/cli/generate.ts | 2 +- .../generate.ts => generator/generator.ts} | 293 ++++++++++++------ src/lib/ResultSet/ResultSet.test-d.ts | 89 ++++++ src/lib/ResultSet/ResultSet.ts | 49 +++ src/lib/ResultSet/_ResultSet.ts | 1 + src/lib/ResultSetReference.ts | 183 +++++++++++ src/lib/SelectionSet.ts | 62 ---- .../lib/SelectionSet/SelectionSet.test-d.ts | 6 +- src/lib/SelectionSet/SelectionSet.ts | 117 +++++++ src/lib/SelectionSet/_SelectionSet.ts | 1 + src/lib/builder.ts | 13 - src/lib/client.ts | 37 +++ src/lib/schema/Schema.ts | 36 +++ src/lib/schema/_Schema.ts | 1 + src/lib/schemaTypes.ts | 60 ---- tests/builder/_/schema.ts | 273 ++++++++++------ .../__snapshots__/generate.test.ts.snap | 123 ++++++-- tests/builder/generate.test.ts | 6 +- 18 files changed, 984 insertions(+), 368 deletions(-) rename src/{lib/generate.ts => generator/generator.ts} (54%) create mode 100644 src/lib/ResultSet/ResultSet.test-d.ts create mode 100644 src/lib/ResultSet/ResultSet.ts create mode 100644 src/lib/ResultSet/_ResultSet.ts create mode 100644 src/lib/ResultSetReference.ts delete mode 100644 src/lib/SelectionSet.ts rename tests/builder/types/selectionSet.test-d.ts => src/lib/SelectionSet/SelectionSet.test-d.ts (96%) create mode 100644 src/lib/SelectionSet/SelectionSet.ts create mode 100644 src/lib/SelectionSet/_SelectionSet.ts delete mode 100644 src/lib/builder.ts create mode 100644 src/lib/client.ts create mode 100644 src/lib/schema/Schema.ts create mode 100644 src/lib/schema/_Schema.ts delete mode 100644 src/lib/schemaTypes.ts diff --git a/src/cli/generate.ts b/src/cli/generate.ts index 282985b50..a627597c9 100644 --- a/src/cli/generate.ts +++ b/src/cli/generate.ts @@ -1,4 +1,4 @@ -import { generateFile } from '../lib/generate.js' +import { generateFile } from '../generator/generator.js' await generateFile({ schemaPath: `./examples/schema.graphql`, diff --git a/src/lib/generate.ts b/src/generator/generator.ts similarity index 54% rename from src/lib/generate.ts rename to src/generator/generator.ts index 72c40b2dc..9ec3b1bf7 100644 --- a/src/lib/generate.ts +++ b/src/generator/generator.ts @@ -10,16 +10,16 @@ import { GraphQLNonNull, GraphQLObjectType, isEnumType } from 'graphql' import { buildSchema } from 'graphql' import _ from 'json-bigint' import fs from 'node:fs/promises' -import { Code } from './Code.js' -import type { AnyClass, AnyField, AnyNamedClassName, Describable, NameToClassNamedType } from './graphql.js' +import { Code } from '../lib/Code.js' +import type { AnyClass, AnyField, AnyNamedClassName, Describable, NameToClassNamedType } from '../lib/graphql.js' import { getNodeDisplayName, getTypeMapByKind, isDeprecatableNode, isGraphQLOutputField, type NameToClass, -} from './graphql.js' -import { entries, values } from './prelude.js' +} from '../lib/graphql.js' +import { entries, values } from '../lib/prelude.js' const namespaceNames = { GraphQLEnumType: `Enum`, @@ -30,13 +30,19 @@ const namespaceNames = { GraphQLUnionType: `Union`, } satisfies Record -type AnyGraphQLFieldsType = GraphQLObjectType | GraphQLInterfaceType | GraphQLInputObjectType +type AnyGraphQLFieldsType = + | GraphQLObjectType + | GraphQLInterfaceType + | GraphQLInputObjectType -const definePointerRenderers = <$Renderers extends { [ClassName in keyof NameToClass]: any }>( +const definePointerRenderers = < + $Renderers extends { [ClassName in keyof NameToClass]: any }, +>( renderers: { [ClassName in keyof $Renderers]: ( config: Config, - node: ClassName extends keyof NameToClass ? InstanceType : never, + node: ClassName extends keyof NameToClass ? InstanceType + : never, ) => string }, ) => renderers @@ -73,14 +79,21 @@ const defineConcreteRenderers = < const dispatchToPointerRenderer = (config: Config, node: AnyClass): string => { // @ts-expect-error lookup const renderer = pointerRenderers[node.constructor.name] // eslint-disable-line - if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) + if (!renderer) { + throw new Error(`No renderer found for class: ${node.constructor.name}`) + } return renderer(config, node) // eslint-disable-line } -const dispatchToConcreteRenderer = (config: Config, node: GraphQLNamedType): string => { +const dispatchToConcreteRenderer = ( + config: Config, + node: GraphQLNamedType, +): string => { // @ts-expect-error lookup const renderer = concreteRenderers[node.constructor.name] // eslint-disable-line - if (!renderer) throw new Error(`No renderer found for class: ${node.constructor.name}`) + if (!renderer) { + throw new Error(`No renderer found for class: ${node.constructor.name}`) + } return renderer(config, node) // eslint-disable-line } @@ -109,15 +122,18 @@ const concreteRenderers = defineConcreteRenderers({ GraphQLInputObjectType: (config, node) => Code.TSDoc( getDocumentation(config, node), - Code.export$(Code.interface$( - node.name, - renderFields(config, node), - )), + Code.export$(Code.interface$(node.name, renderFields(config, node))), ), GraphQLInterfaceType: (config, node) => - Code.TSDoc(getDocumentation(config, node), Code.export$(Code.interface$(node.name, renderFields(config, node)))), + Code.TSDoc( + getDocumentation(config, node), + Code.export$(Code.interface$(node.name, renderFields(config, node))), + ), GraphQLObjectType: (config, node) => - Code.TSDoc(getDocumentation(config, node), Code.export$(Code.interface$(node.name, renderFields(config, node)))), + Code.TSDoc( + getDocumentation(config, node), + Code.export$(Code.interface$(node.name, renderFields(config, node))), + ), GraphQLScalarType: () => ``, GraphQLUnionType: (config, node) => Code.TSDoc( @@ -125,7 +141,11 @@ const concreteRenderers = defineConcreteRenderers({ Code.export$( Code.union( node.name, - node.getTypes().map((_) => dispatchToPointerRenderer(config, _) + `& { $$union:true}`), + node + .getTypes() + .map( + (_) => dispatchToPointerRenderer(config, _) + `& { $$union:true}`, + ), ), ), ), @@ -140,14 +160,21 @@ const getDocumentation = (config: Config, node: Describable) => { : null const enumMemberDescriptions: string[] = isEnumType(node) - ? node.getValues() - .map(_ => { - const deprecationDescription = _.deprecationReason ? `(DEPRECATED: ${_.deprecationReason})` : null + ? node + .getValues() + .map((_) => { + const deprecationDescription = _.deprecationReason + ? `(DEPRECATED: ${_.deprecationReason})` + : null const generalDescription = _.description ? _.description - : (config.TSDoc.noDocPolicy === `message` ? `Missing description.` : null) + : config.TSDoc.noDocPolicy === `message` + ? `Missing description.` + : null if (!generalDescription && !deprecationDescription) return null - const content = [generalDescription, deprecationDescription].filter(_ => _ !== null).join(` `) + const content = [generalDescription, deprecationDescription] + .filter((_) => _ !== null) + .join(` `) return [_, content] as const }) .filter((_): _ is [GraphQLEnumValue, string] => _ !== null) @@ -159,8 +186,15 @@ const getDocumentation = (config: Config, node: Describable) => { const enumMemberDescription = enumMemberDescriptions.length > 0 ? `Members\n${enumMemberDescriptions.join(`\n`)}` : null - if (!enumMemberDescription && !generalDescription && !deprecationDescription) return null - const content = [generalDescription, enumMemberDescription, deprecationDescription].filter(_ => _ !== null) + if (!enumMemberDescription && !generalDescription && !deprecationDescription) { + return null + } + const content = [ + generalDescription, + enumMemberDescription, + deprecationDescription, + ] + .filter((_) => _ !== null) .join(`\n\n`) return content } @@ -168,46 +202,74 @@ const getDocumentation = (config: Config, node: Describable) => { const defaultDescription = (node: Describable) => `There is no documentation for this ${getNodeDisplayName(node)}.` const renderFields = (config: Config, node: AnyGraphQLFieldsType): string => { - const __typenameField = node instanceof GraphQLObjectType ? [Code.field(`__typename`, `"${node.name}"`)] : [] - return Code.fields( - [ - ...__typenameField, - ...values(node.getFields()).map((field) => - Code.TSDoc(getDocumentation(config, field), Code.field(field.name, renderField(config, field))) + const __typenameField = node instanceof GraphQLObjectType + ? [ + Code.field( + `__typename`, + Code.object(Code.fields([ + Code.field(`type`, `"${node.name}"`), + Code.field(`nullable`, `false`), + Code.field(`args`, `null`), + ])), ), - ], - ) + ] + : [] + return Code.fields([ + ...__typenameField, + ...values(node.getFields()).map((field) => + Code.TSDoc( + getDocumentation(config, field), + Code.field(field.name, renderField(config, field)), + ) + ), + ]) } const renderField = (config: Config, field: AnyField): string => { const { node, nullable } = unwrapNonNull(field.type) - const fieldBase = dispatchToPointerRenderer(config, node) + const type = dispatchToPointerRenderer(config, node) - const args = isGraphQLOutputField(field) && field.args.length > 0 ? renderArgs(config, field.args) : null - const fieldWithArgs = args ? Code.intersection(fieldBase, args) : fieldBase - - const fieldWithArgsWithNullable = nullable ? Code.nullable(fieldWithArgs) : fieldWithArgs + const args = isGraphQLOutputField(field) && field.args.length > 0 + ? renderArgs(config, field.args) + : null + // const fieldWithArgs = args ? Code.intersection(fieldBase, args) : fieldBase - return fieldWithArgsWithNullable + return Code.object(Code.fields([ + Code.field(`type`, type), + Code.field(`nullable`, nullable ? `true` : `false`), + Code.field(`args`, args ?? `null`), + ])) } const renderArgs = (config: Config, args: readonly GraphQLArgument[]) => { let hasRequiredArgs = false - const argsRendered = Code.object(Code.fields(args.map(arg => { - const { node, nullable } = unwrapNonNull(arg.type) - hasRequiredArgs = hasRequiredArgs || !nullable - return Code.field( - arg.name, - nullable ? Code.nullable(dispatchToPointerRenderer(config, node)) : dispatchToPointerRenderer(config, node), - { optional: nullable }, - ) - }))) - const allArgsOptional = !hasRequiredArgs - return Code.object(Code.field(`$`, argsRendered, { optional: allArgsOptional })) + const argsRendered = Code.object( + Code.fields( + args.map((arg) => { + const { node, nullable } = unwrapNonNull(arg.type) + hasRequiredArgs = hasRequiredArgs || !nullable + return Code.field( + arg.name, + nullable + ? Code.nullable(dispatchToPointerRenderer(config, node)) + : dispatchToPointerRenderer(config, node), + { optional: nullable }, + ) + }), + ), + ) + return Code.object( + Code.fields([ + Code.field(`type`, argsRendered), + Code.field(`allOptional`, hasRequiredArgs ? `false` : `true`), + ]), + ) } -const unwrapNonNull = (node: AnyClass): { node: AnyClass; nullable: boolean } => { +const unwrapNonNull = ( + node: AnyClass, +): { node: AnyClass; nullable: boolean } => { const [nodeUnwrapped, nullable] = node instanceof GraphQLNonNull ? [node.ofType, false] : [node, true] return { node: nodeUnwrapped, nullable } } @@ -250,78 +312,105 @@ export const generateCode = (input: Input) => { const typeMapByKind = getTypeMapByKind(schema) const config = resolveOptions(input.options) - const hasQuery = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Query`) - const hasMutation = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Mutation`) - const hasSubscription = typeMapByKind.GraphQLRootTypes.find((_) => _.name === `Subscription`) + const hasQuery = typeMapByKind.GraphQLRootTypes.find( + (_) => _.name === `Query`, + ) + const hasMutation = typeMapByKind.GraphQLRootTypes.find( + (_) => _.name === `Mutation`, + ) + const hasSubscription = typeMapByKind.GraphQLRootTypes.find( + (_) => _.name === `Subscription`, + ) let code = `` - code += Code.export$(Code.namespace( - `$`, - Code.group( - Code.export$( - Code.interface$( - `Index`, - Code.fields([ - Code.field( - `Root`, - Code.object( - Code.fields([ - Code.field(`Query`, hasQuery ? `Root.Query` : `null`), - Code.field(`Mutation`, hasMutation ? `Root.Mutation` : `null`), - Code.field(`Subscription`, hasSubscription ? `Root.Subscription` : `null`), - ]), + code += Code.export$( + Code.namespace( + `$`, + Code.group( + Code.export$( + Code.interface$( + `Index`, + Code.fields([ + Code.field( + `Root`, + Code.object( + Code.fields([ + Code.field(`Query`, hasQuery ? `Root.Query` : `null`), + Code.field( + `Mutation`, + hasMutation ? `Root.Mutation` : `null`, + ), + Code.field( + `Subscription`, + hasSubscription ? `Root.Subscription` : `null`, + ), + ]), + ), ), - ), - Code.field( - `unions`, - Code.object( - Code.fields([ - Code.field( - `Union`, - typeMapByKind.GraphQLUnionType.length > 0 - ? Code.unionItems(typeMapByKind.GraphQLUnionType.map(_ => `Union.${_.name}`)) - : `null`, - ), - ]), + Code.field( + `unions`, + Code.object( + Code.fields([ + Code.field( + `Union`, + typeMapByKind.GraphQLUnionType.length > 0 + ? Code.unionItems( + typeMapByKind.GraphQLUnionType.map( + (_) => `Union.${_.name}`, + ), + ) + : `null`, + ), + ]), + ), ), - ), - Code.field(`scalars`, `Scalars`), - ]), + Code.field(`scalars`, `Scalars`), + ]), + ), ), - ), - Code.export$(Code.interface$( - `Scalars`, - ` + Code.export$( + Code.interface$( + `Scalars`, + ` ${ - typeMapByKind.GraphQLScalarType.map((_) => { - // todo strict mode where instead of falling back to "any" we throw an error - const type = scalarTypeMap[_.name] || `string` - return Code.field(_.name, type) - }).join(`\n`) - } + typeMapByKind.GraphQLScalarType.map((_) => { + // todo strict mode where instead of falling back to "any" we throw an error + const type = scalarTypeMap[_.name] || `string` + return Code.field(_.name, type) + }).join(`\n`) + } `, - )), + ), + ), + ), ), - )) + ) for (const [name, types] of entries(typeMapByKind)) { if (name === `GraphQLScalarType`) continue const namespaceName = name === `GraphQLRootTypes` ? `Root` : namespaceNames[name] code += Code.commentSectionTitle(namespaceName) - code += Code.export$(Code.namespace( - namespaceName, - types.length === 0 - ? `// -- no types --\n` - : types.map(_ => dispatchToConcreteRenderer(config, _)).join(`\n\n`), - )) + code += Code.export$( + Code.namespace( + namespaceName, + types.length === 0 + ? `// -- no types --\n` + : types + .map((_) => dispatchToConcreteRenderer(config, _)) + .join(`\n\n`), + ), + ) } return code } -export const generateFile = async (params: { schemaPath: string; typeScriptPath: string }) => { +export const generateFile = async (params: { + schemaPath: string + typeScriptPath: string +}) => { // todo use @dprint/formatter const schemaSource = await fs.readFile(params.schemaPath, `utf8`) const code = generateCode({ schemaSource }) diff --git a/src/lib/ResultSet/ResultSet.test-d.ts b/src/lib/ResultSet/ResultSet.test-d.ts new file mode 100644 index 000000000..d3cf15b16 --- /dev/null +++ b/src/lib/ResultSet/ResultSet.test-d.ts @@ -0,0 +1,89 @@ +import { expectTypeOf, test } from 'vitest' +import type * as Schema from '../../../tests/builder/_/schema.js' +import type { ResultSet } from './_ResultSet.js' + +type I = Schema.$.Index + +// dprint-ignore +test(`general`, () => { + // scalar + expectTypeOf>().toEqualTypeOf<{ string: string | null }>() + + // Arguments + // scalar + expectTypeOf>().toEqualTypeOf<{ stringWithArgs: null | string }>() + + // error: unknown field + expectTypeOf>().toEqualTypeOf<{ string2: ResultSet.Errors.UnknownFieldName<'string2', Schema.Root.Query> }>() + + // assertType>({string:''}) +}) +// import { describe, expectTypeOf, test } from 'vitest' +// import type { TSError } from '../../../src/lib/TSError.js' +// // import type { TSError } from '~/lib/prelude/index.js' +// // import type * as GenqlTypes from '../genql/schema.js' +// // import type { SelectQuery } from './inferSelectionResult.js' + +// test(`utilities`, () => { +// // expectTypeOf>().toEqualTypeOf<{__typename:true}|{message:true}>() // prettier-ignore +// }) + +// // dprint-ignore +// describe(`direct`, () => { +// test(`interface`, () => { +// expectTypeOf>().toEqualTypeOf<{__typename: 'OfferAbstract' | 'OfferValue' | 'OfferResourceProperty' | 'OfferResourceAggregation'}>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ id: string }>() +// expectTypeOf>().toEqualTypeOf<{} | { id: string }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{}|{id:string}>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{}|{feature:{id:string}}>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{}|{__typename:'OfferAbstract'}>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{}|{__typename:'OfferAbstract'}|{__typename:'OfferValue'}>() // prettier-ignore +// // expectTypeOf<>().toEqualTypeOf<>() // prettier-ignore +// }) +// }) + +// // dprint-ignore +// describe(`Query`, () => { +// test(`object`, () => { +// expectTypeOf>().toEqualTypeOf<{}>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ me: { __typename: 'Me' } }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ me: { user: { id: string } } }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ me: { user: { __typename: 'User'; id: string; displayName: string | null; email: string; handle: string | null; image: string | null } } }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ me: { user: {} } }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ me: {} }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ me: {} }>() // prettier-ignore +// }) +// test(`object list`, () => { +// expectTypeOf>().toEqualTypeOf<{ me: { workspaces: { id: string }[] } }>() // prettier-ignore +// }) +// test(`scalar list`, () => { +// expectTypeOf>().toEqualTypeOf<{ plan: {}|{offers:{platform:{support:{limit:{allowed:string[]}|{}|null}}}}}>() // prettier-ignore +// }) + +// test(`union`, () => { +// expectTypeOf>().toEqualTypeOf<{ project: | { __typename: 'Project' } | { __typename: 'ErrorInternal' } | { __typename: 'ErrorUserBusinessNotAuthorized' } | { __typename: 'ErrorUserBusinessResourceNotFound' } }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ project: | { __typename: 'Project'; id: string } | { __typename: 'ErrorInternal' } | { __typename: 'ErrorUserBusinessNotAuthorized' } | { __typename: 'ErrorUserBusinessResourceNotFound' } }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ project: {} | { id: string } }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ project: {} | { message: string } | { id: string } }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ project: {} | { id: string } }>() // prettier-ignore +// expectTypeOf>().toMatchTypeOf<{ project: {} }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ project: {} | { id: string } }>() // prettier-ignore +// // expectTypeOf<>().toEqualTypeOf<>() // prettier-ignore +// }) +// test(`custom scalar`, () => { +// expectTypeOf>().toEqualTypeOf<{ project: {} | { createdAt: string } }>() // prettier-ignore +// }) +// test(`interface`, () => { +// // expectTypeOf<>().toEqualTypeOf<>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ project: {} | { message: string }}>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ project: { message: string } | { id: string }}>() // prettier-ignore +// }) +// test(`nullable`, () => { +// expectTypeOf>().toEqualTypeOf<{ project: {} | { pulse: { databaseLink: {} | null } } }>() // prettier-ignore +// }) +// test(`errors`, () => { +// // expectTypeOf<>().toEqualTypeOf<>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ foo: TSError<'SelectObjektField', 'object selection field "foo" does not exist on schema object "Query"'> }>() // prettier-ignore +// expectTypeOf>().toEqualTypeOf<{ me: { bar: TSError<'SelectObjektField', 'object selection field "bar" does not exist on schema object "Me"'> } }>() // prettier-ignore +// }) +// }) diff --git a/src/lib/ResultSet/ResultSet.ts b/src/lib/ResultSet/ResultSet.ts new file mode 100644 index 000000000..53bdf4dae --- /dev/null +++ b/src/lib/ResultSet/ResultSet.ts @@ -0,0 +1,49 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import type { Schema } from '../schema/_Schema.js' +import type * as SelectionSet from '../SelectionSet/SelectionSet.js' +import type { TSError } from '../TSError.js' + +export type Query<$SelectionSetQuery extends object, $Index extends Schema.Index> = Object< + $SelectionSetQuery, + Exclude<$Index['Root']['Query'], null> +> + +// // dprint-ignore +// export type ResultSet<$SelectionSet extends object, $Node extends Schema.Node> = +// $Node extends Schema.Nullable +// ? null | ResultSet<$SelectionSet, Exclude<$Node, Schema.Nullable>> +// : ResultSetObject<$SelectionSet, $Node> + +export type Object<$SelectionSet extends object, $Object extends Schema.Object> = { + [ + $Key in keyof SelectionSet.OmitArgs<$SelectionSet> & string + ]: $Key extends keyof $Object ? Field> + : Errors.UnknownFieldName<$Key,$Object> +} + +type Field<$Field extends Schema.Field> = $Field['type'] | ($Field['nullable'] extends true ? null : never) + +export namespace Errors { + export type UnknownFieldName<$FieldName extends string, $Node extends Schema.Object> = TSError< + 'ResultSetObject', + `field "${$FieldName}" does not exist on schema object "${$Node['__typename']['type']}"` + > +} + +// type SelectField<$Objekt extends Schema.Object, $SelectionObjekt extends SelectionObjekt, $FieldName extends keyof $SelectionObjekt & string> = +// $FieldName extends keyof $Objekt ? $Objekt[$FieldName] extends Node ? $SelectionObjekt[$FieldName] extends SelectionScalar ? SelectScalar<$Objekt[$FieldName], $SelectionObjekt[$FieldName]> +// : $SelectionObjekt[$FieldName] extends SelectionObjekt ? SelectObjekt<$Objekt[$FieldName], $SelectionObjekt[$FieldName]> +// : TSError<'SelectionObjektField', `selection object field "${$FieldName}" is of unknown type.`> +// : [$SelectionObjekt,$FieldName,$Objekt[$FieldName],TSError<'SelectObjektField', `object selection field "${$FieldName}" on schema object "${$Objekt['__typename']}" is not a Node.`>] +// // : $Field extends `on_${infer $TypeName}` ? TSError<'SelectObjektField', `fragment field "on_${$TypeName}" should be handled by caller.`> +// : TSError<'SelectObjektField', `object selection field "${$FieldName}" does not exist on schema object "${$Objekt['__typename']}"`> + +// $Node extends Nullable ? null | SelectObjekt, $Selection> +// : $Node extends Scalar ? TSError<'SelectObjekt','$Node is Scalar (should be Object).'> +// : $Node extends List ? TSError<'SelectObjekt','$Node is List (should be Object).'> +// : $Node extends List ? SelectObjekt_<$Node[number], $Selection>[] +// // : $Node extends Interface ? SelectInterface<$Node, $Selection> +// : $Node extends Objekt ? SelectObjekt_<$Node, $Selection> +// : TSError<'SelectObjekt','$Node is unknown type (should be Objekt).', { $Node: $Node }> +// : TSError<'SelectObjekt','$Selection is not an object.', { $Selection: $Selection }> diff --git a/src/lib/ResultSet/_ResultSet.ts b/src/lib/ResultSet/_ResultSet.ts new file mode 100644 index 000000000..fb546c609 --- /dev/null +++ b/src/lib/ResultSet/_ResultSet.ts @@ -0,0 +1 @@ +export * as ResultSet from './ResultSet.js' diff --git a/src/lib/ResultSetReference.ts b/src/lib/ResultSetReference.ts new file mode 100644 index 000000000..cfe0bac45 --- /dev/null +++ b/src/lib/ResultSetReference.ts @@ -0,0 +1,183 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +/** + * The follow types work around limitations in the GenQL types with regards to union types. + * @see https://github.com/remorses/genql/issues/108 + */ +import type { UnionToIntersection } from 'type-fest' +import type { ConditionalSimplifyDeep } from 'type-fest/source/conditional-simplify' +import type { Values } from '~/lib/prelude' +import { type TSError, type ValuesOrEmptyObject } from '~/lib/prelude' +import type * as GenqlTypes from '../genql/schema.js' +import type { FieldNameConstants, Interface, List, Node, Nullable, Objekt, Scalar } from '../types' +import type { GetSelection } from './utilities.js' + +type SimplifyDeep = ConditionalSimplifyDeep | Date, object> // eslint-disable-line + +type SelectionRoot = SelectionObjekt +type SelectionScalar = boolean +type SelectionObjekt = object + +export type SelectMutation<$SelectionRoot extends SelectionRoot> = Select<'Mutation', $SelectionRoot> + +export type SelectQuery<$SelectionRoot extends SelectionRoot> = Select<'Query', $SelectionRoot> + +export type Selection< + $TypeName extends keyof GenqlTypes.Selections, + $SelectionObjekt extends GetSelection<$TypeName>, +> = $SelectionObjekt + +export type Select< + $TypeName extends keyof GenqlTypes.Selections, + $SelectionObjekt extends GetSelection<$TypeName>, +> // @ts-expect-error fixme + = SimplifyDeep> + +export type SelectOn< + $Node extends Objekt, + $SelectionObjekt extends GetSelection<$Node['__typename']>, +> = SimplifyDeep> + +// dprint-ignore +type SelectObjekt<$Node extends Node, $Selection> = + $Selection extends object ? $Node extends Nullable ? null | SelectObjekt, $Selection> + : $Node extends Scalar ? TSError<'SelectObjekt','$Node is Scalar (should be Object).'> + : $Node extends List ? TSError<'SelectObjekt','$Node is List (should be Object).'> + : $Node extends List ? SelectObjekt_<$Node[number], $Selection>[] + // : $Node extends Interface ? SelectInterface<$Node, $Selection> + : $Node extends Objekt ? SelectObjekt_<$Node, $Selection> + : TSError<'SelectObjekt','$Node is unknown type (should be Objekt).', { $Node: $Node }> + : TSError<'SelectObjekt','$Selection is not an object.', { $Selection: $Selection }> + +// dprint-ignore +type SelectObjekt_<$Node extends Objekt, $Selection extends SelectionObjekt> = + + /** + * Nothing to do, the selection set is empty (e.g.: \{\} ) + */ + keyof $Selection extends never ? {} + : + /** + * Handle "regular" selection set fields (not a union or interface) + */ + { + [ + $Field in keyof $Selection & string as + $Field extends FieldNameConstants.__scalar ? never + : $Field extends FieldNameConstants.__args ? never + : $Field extends `on_${infer _}` ? never + : $Selection[$Field] extends false ? never + : $Field + ]: + SelectObjektField<$Node, $Selection, $Field> + } + + & + + /** + * Handle the __scalar: true feature. This special field selector means + * select _all_ scalar fields of the object. + */ + ( + IsSelectsField extends true + ? { + [$Field in keyof $Node & string as $Node[$Field] extends Scalar | Nullable ? $Field : never]: $Node[$Field] + } + : {} // eslint-disable-line + ) + + & + + /** + * Handle selection sets for interfaces and union type members. + * + * @remarks This selection set needs to be "hoisted" into the parent selection set. + * Hence the TypeScript intersection ("&") above. + */ + UnionToIntersection + : $InterfaceOrObjectTypeName extends keyof GenqlTypes.Objects ? SelectObjekt + : TSError<'SelectObjekt_', `The given type name in on_TYPE_NAME of ${$InterfaceOrObjectTypeName} does not correspond to any know object or interface name.`> + : never + }>> + + & + + ( + $Node extends Interface ? + // FieldNameConstants.__typenameInterface extends keyof $Node ? + // $Node[FieldNameConstants.__typenameInterface] extends keyof GenqlTypes.Interfaces.$Guide ? + // Values extends Objekt ? + // @ts-expect-error fixme + SelectAgainstInterface, $Selection> + : {} + // : never + // : never + // : never + ) + +// dprint-ignore +type SelectAgainstInterface< + $Implementor extends Objekt, + $Selection, +> = +$Implementor extends any ? // force distribution over union type + `on_${$Implementor['__typename']}` extends keyof $Selection + ? SelectObjekt<$Implementor, $Selection[`on_${$Implementor['__typename']}`]> + : {} +: never + +// dprint-ignore +type SelectObjektField<$Objekt extends Objekt, $SelectionObjekt extends SelectionObjekt, $Field extends keyof $SelectionObjekt & string> = + $Field extends keyof $Objekt ? $Objekt[$Field] extends Node ? $SelectionObjekt[$Field] extends SelectionScalar ? SelectScalar<$Objekt[$Field], $SelectionObjekt[$Field]> + : $SelectionObjekt[$Field] extends SelectionObjekt ? SelectObjekt<$Objekt[$Field], $SelectionObjekt[$Field]> + : TSError<'SelectionObjektField', `selection object field "${$Field}" is of unknown type.`> + : [$SelectionObjekt,$Field,$Objekt[$Field],TSError<'SelectObjektField', `object selection field "${$Field}" on schema object "${$Objekt['__typename']}" is not a Node.`>] + // : $Field extends `on_${infer $TypeName}` ? TSError<'SelectObjektField', `fragment field "on_${$TypeName}" should be handled by caller.`> + : TSError<'SelectObjektField', `object selection field "${$Field}" does not exist on schema object "${$Objekt['__typename']}"`> + +// dprint-ignore +type SelectScalar<$Node extends Node, $SelectionScalar extends SelectionScalar> = + $Node extends Nullable ? null | SelectScalar, $SelectionScalar> + : $Node extends List ? SelectScalar<$Node[number], $SelectionScalar>[] + : $Node extends Objekt ? TSError<'SelectScalar','$Node is Objekt.'> + : $Node extends Scalar ? $SelectionScalar extends true ? $Node + : never + : TSError<'SelectScalar','$Node is of unknown type.'> + +type IsSelectsField< + $Field extends string, + $SelectionObjekt extends SelectionObjekt, +> = $Field extends keyof $SelectionObjekt ? ($SelectionObjekt[$Field] extends true ? true : false) : false + +// // dprint-ignore +// type GetInterfaceImplementorSelections<$Interface extends Interface, $Selection> = +// $Selection[GetInterfaceImplementorSelections_<$Interface, keyof $Selection & string>] +// // dprint-ignore +// type GetInterfaceImplementorSelections_<$Interface extends Interface, $SelectionField extends string> = +// $SelectionField extends `on_${infer _ extends keyof GenqlTypes.Interfaces.$Guide[$Interface[FieldNameConstants.__typenameInterface]]['implementors'] & string}` +// ? $SelectionField +// : never diff --git a/src/lib/SelectionSet.ts b/src/lib/SelectionSet.ts deleted file mode 100644 index e2887a51d..000000000 --- a/src/lib/SelectionSet.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { MaybeList, NonEmptyString } from './prelude.js' -import type { - ClientIndicator, - ExcludeNull, - FieldDirectives, - Indicator, - NoArgsIndicator, - ObjectType, - OmitUnionBrand, - ScalarType, - SchemaIndex, - UnionType, -} from './schemaTypes.js' -import type { TSError } from './TSError.js' - -type OmitArgs = Omit - -// dprint-ignore -export type SelectionSet = - T extends ScalarType ? Indicator : - ExcludeNull extends UnionType ? SelectionSetUnion, $Index> & { __typename?: NoArgsIndicator } : - T extends ObjectType ? SelectionSetObject, $Index> & Arguments : - TSError<'SelectionSet', 'T is not a SelectableFieldsType', { T:T }> - -type SelectionSetObject<$Object extends ObjectType, $Index extends SchemaIndex> = - & { - [Key in keyof OmitArgs<$Object>]?: SelectionSet, $Index> - } - /** - * Alias support. - * Allow every field to also be given as a key with this pattern `_as_: ...` - */ - & { - [Key in keyof OmitArgs<$Object> as `${keyof OmitArgs<$Object> & string}_as_${NonEmptyString}`]?: SelectionSet< - ExcludeNull<$Object[Key]>, - $Index - > - } - & FieldDirectives - /** - * Inline fragments for field groups. - * @see https://spec.graphql.org/draft/#sec-Inline-Fragments - */ - & { - ___?: MaybeList> - } - /** - * Special property to select all scalars. - */ - & { $scalars?: ClientIndicator } - -type Arguments<$FieldType> = '$' extends keyof $FieldType ? Pick<$FieldType, '$'> : {} - -// TODO why does $object not get passed to this in a distributed way? -type SelectionSetUnion<$Object extends ObjectType, $Index extends SchemaIndex> = - & { - [Key in $Object['__typename'] as `on${Capitalize}`]?: SelectionSet< - OmitUnionBrand>, - $Index - > - } - & { __typename?: NoArgsIndicator } diff --git a/tests/builder/types/selectionSet.test-d.ts b/src/lib/SelectionSet/SelectionSet.test-d.ts similarity index 96% rename from tests/builder/types/selectionSet.test-d.ts rename to src/lib/SelectionSet/SelectionSet.test-d.ts index 3dba7ce18..6c2b2e55f 100644 --- a/tests/builder/types/selectionSet.test-d.ts +++ b/src/lib/SelectionSet/SelectionSet.test-d.ts @@ -1,8 +1,8 @@ import { assertType, test } from 'vitest' -import type { SelectionSet } from '../../../src/lib/SelectionSet.js' -import type * as Schema from '../_/schema.js' +import type * as Schema from '../../../tests/builder/_/schema.js' +import type { Query } from './SelectionSet.js' -type S = SelectionSet +type S = Query test(`general`, () => { // scalar diff --git a/src/lib/SelectionSet/SelectionSet.ts b/src/lib/SelectionSet/SelectionSet.ts new file mode 100644 index 000000000..528616999 --- /dev/null +++ b/src/lib/SelectionSet/SelectionSet.ts @@ -0,0 +1,117 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import type { MaybeList, NonEmptyString } from '../prelude.js' +import type { Schema } from '../schema/_Schema.js' +import type { TSError } from '../TSError.js' + +export type OmitArgs = Omit + +export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Object + ? SelectionSetObject<$Index['Root']['Query'], $Index> + : never + +export type Mutation<$Index extends Schema.Index> = $Index['Root']['Mutation'] extends Object + ? SelectionSetObject<$Index['Root']['Mutation'], $Index> + : never + +export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscription'] extends Object + ? SelectionSetObject<$Index['Root']['Subscription'], $Index> + : never + +type SelectionSetObject<$Object extends Schema.Object, $Index extends Schema.Index> = + & { + [Key in keyof $Object]?: SelectionSetField, $Index> + } + /** + * Alias support. + * Allow every field to also be given as a key with this pattern `_as_: ...` + */ + & { + [Key in keyof $Object as `${keyof OmitArgs<$Object> & string}_as_${NonEmptyString}`]?: SelectionSetField< + Schema.AsField<$Object[Key]>, + $Index + > + } + & FieldDirectives + /** + * Inline fragments for field groups. + * @see https://spec.graphql.org/draft/#sec-Inline-Fragments + */ + & { + ___?: MaybeList> + } + /** + * Special property to select all scalars. + */ + & { $scalars?: ClientIndicator } + +// dprint-ignore +export type SelectionSetField<$Field extends Schema.Field, $Index extends Schema.Index> = +$Field extends Schema.ScalarField ? Indicator<$Field> : +$Field extends Schema.unionField ? SelectionSetUnion<$Field['type'], $Index> : +$Field extends Schema.ObjectField ? SelectionSetObject<$Field['type'], $Index> & Arguments<$Field> : + TSError<'SelectionSetField', '$Field case not handled', { $Field:$Field }> + +type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.Args + ? $Field['args']['allOptional'] extends true ? { + $?: $Field['args']['type'] + } + : { + $: $Field['args']['type'] + } + : {} + +// TODO why does $object not get passed to this in a distributed way? +type SelectionSetUnion<$Object extends Schema.Union, $Index extends Schema.Index> = + & { + [Key in $Object['__typename']['type'] as `on${Capitalize}`]?: SelectionSetObject< + Schema.OmitUnionBrand>, + $Index + > + } + & { __typename?: NoArgsIndicator } + +/** + * Helpers + * --------------------------------------------------------------------------------------------------- + */ + +/** + * Should this field be selected? + */ +export type ClientIndicator = boolean | 1 | 0 + +/** + * Field selection in general, with directives support too. + * If a field directive is given as an indicator then it implies "select this" e.g. `true`/`1`. + * Of course the semantics of the directive may change the derived type (e.g. `skip` means the field might not show up in the result set) + */ +export type NoArgsIndicator = ClientIndicator | FieldDirectives + +// dprint-ignore +export type Indicator<$FieldType extends Schema.Field> = +$FieldType['args'] extends Schema.Args ? $FieldType['args']['allOptional'] extends true ? ({ $?: $FieldType['args']['type'] } & FieldDirectives) | ClientIndicator + : ({ $: $FieldType['args']['type'] } & FieldDirectives) + : NoArgsIndicator + +/** + * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives + */ +export interface FieldDirectives { + /** + * https://spec.graphql.org/draft/#sec--skip + */ + $skip?: boolean | { if?: boolean } + /** + * https://spec.graphql.org/draft/#sec--include + */ + $include?: boolean | { if?: boolean } + /** + * @see https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#defer + */ + $defer?: boolean | { if?: boolean; label?: string } + /** + * @see https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#stream + */ + $stream?: boolean | { if?: boolean; label?: string; initialCount?: number } +} diff --git a/src/lib/SelectionSet/_SelectionSet.ts b/src/lib/SelectionSet/_SelectionSet.ts new file mode 100644 index 000000000..ff647a1f3 --- /dev/null +++ b/src/lib/SelectionSet/_SelectionSet.ts @@ -0,0 +1 @@ +export * as SelectionSet from './SelectionSet.js' diff --git a/src/lib/builder.ts b/src/lib/builder.ts deleted file mode 100644 index e421528c3..000000000 --- a/src/lib/builder.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { SchemaIndex } from './schemaTypes.js' -import type { SelectionSet } from './SelectionSet.js' - -export type Query = <$SchemaIndex extends SchemaIndex>( - document: $SchemaIndex['Root']['Query'] extends null ? null - : SelectionSet<$SchemaIndex['Root']['Query'], $SchemaIndex>, -) => Promise - -export const query: Query = async (document) => { - // todo - document - await Promise.resolve() -} diff --git a/src/lib/client.ts b/src/lib/client.ts new file mode 100644 index 000000000..8c60b4349 --- /dev/null +++ b/src/lib/client.ts @@ -0,0 +1,37 @@ +import type { Query } from './ResultSet/ResultSet.js' +import type { Index } from './schema/Schema.js' +import type { SelectionSet } from './SelectionSet/_SelectionSet.js' + +export type Client<$SchemaIndex extends Index> = + & ( + $SchemaIndex['Root']['Query'] extends null ? { + // dprint-ignore + query: <$SelectionSet extends SelectionSet.Query<$SchemaIndex>> + (selectionSet: $SelectionSet) => Promise> + } + : unknown + ) + & ( + $SchemaIndex['Root']['Mutation'] extends null ? { + // dprint-ignore + mutation: <$SelectionSet extends SelectionSet.Mutation<$SchemaIndex>> + (selectionSet: $SelectionSet) => Promise> + } + : unknown + ) + & ( + $SchemaIndex['Root']['Subscription'] extends null ? { + // dprint-ignore + subscription: <$SelectionSet extends SelectionSet.Subscription<$SchemaIndex>> + (selectionSet: $SelectionSet) => Promise> + } + : unknown + ) + +interface Input { + url: URL | string +} + +export const create = <$SchemaIndex extends Index>(input: Input): Client<$SchemaIndex> => { + return 1 as any +} diff --git a/src/lib/schema/Schema.ts b/src/lib/schema/Schema.ts new file mode 100644 index 000000000..d2fa23d75 --- /dev/null +++ b/src/lib/schema/Schema.ts @@ -0,0 +1,36 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +export interface Index { + unions: { + Union: null | Union + // root: null | Object + } + Root: { + Query: null | Object + Mutation: null | Object + Subscription: null | Object + } + scalars: object +} + +export type Nullable = null +// todo needs to be extensible for custom scalars... +export type Scalar = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] +export type Object = { __typename: { args: null; type: string } } +export type Union = Object & { $$union: true } +export type Node = Object | Union | Scalar | Nullable + +export type FieldBase<$Type extends Node> = { args: null | Args; type: $Type; nullable: boolean } +export type NullableField = FieldBase +export type ScalarField = FieldBase +export type ObjectField = FieldBase +export type unionField = FieldBase +export type Field = FieldBase + +export type Args = { type: object; allOptional: boolean } + +export type ExcludeNull = Exclude +export type OmitUnionBrand = Omit +export type OmitArgs = Omit + +export type AsField = T extends Field ? T : never diff --git a/src/lib/schema/_Schema.ts b/src/lib/schema/_Schema.ts new file mode 100644 index 000000000..909fa2233 --- /dev/null +++ b/src/lib/schema/_Schema.ts @@ -0,0 +1 @@ +export * as Schema from './Schema.js' diff --git a/src/lib/schemaTypes.ts b/src/lib/schemaTypes.ts deleted file mode 100644 index 8cae5b8ee..000000000 --- a/src/lib/schemaTypes.ts +++ /dev/null @@ -1,60 +0,0 @@ -export interface SchemaIndex { - unions: { - Union: null | ObjectType - // root: null | ObjectType - } - Root: { - Query: null | ObjectType - Mutation: null | ObjectType - Subscription: null | ObjectType - } - scalars: object -} - -// todo needs to be extensible for custom scalars... -export type ScalarType = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] -export type ObjectType = { __typename: string } -export type UnionType = ObjectType & { $$union: true } - -export type ExcludeNull = Exclude -export type OmitUnionBrand = Omit - -/** - * Should this field be selected? - */ -export type ClientIndicator = boolean | 1 | 0 - -/** - * Field selection in general, with directives support too. - * If a field directive is given as an indicator then it implies "select this" e.g. `true`/`1`. - * Of course the semantics of the directive may change the derived type (e.g. `skip` means the field might not show up in the result set) - */ -export type NoArgsIndicator = ClientIndicator | FieldDirectives - -// dprint-ignore -export type Indicator = -'$' extends keyof Type ? undefined extends Type['$'] ? (Pick & FieldDirectives) | ClientIndicator - : (Pick & FieldDirectives) - : NoArgsIndicator - -/** - * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives - */ -export interface FieldDirectives { - /** - * https://spec.graphql.org/draft/#sec--skip - */ - $skip?: boolean | { if?: boolean } - /** - * https://spec.graphql.org/draft/#sec--include - */ - $include?: boolean | { if?: boolean } - /** - * @see https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#defer - */ - $defer?: boolean | { if?: boolean; label?: string } - /** - * @see https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#stream - */ - $stream?: boolean | { if?: boolean; label?: string; initialCount?: number } -} diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 8406873a3..651b582b2 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -1,24 +1,22 @@ export namespace $ { -export interface Index { -Root: { -Query: Root.Query -Mutation: null -Subscription: null -} -unions: { -Union: Union.FooBarUnion -} -scalars: Scalars -} -export interface Scalars { - + export interface Index { + Root: { + Query: Root.Query + Mutation: null + Subscription: null + } + unions: { + Union: Union.FooBarUnion + } + scalars: Scalars + } + export interface Scalars { String: string -Int: number -Float: number -Boolean: boolean -ID: string - -} + Int: number + Float: number + Boolean: boolean + ID: string + } } // ------------------------------------------------------------ // @@ -26,39 +24,74 @@ ID: string // ------------------------------------------------------------ // export namespace Root { -export interface Query { -__typename: "Query" -string: $.Scalars["String"] | null -stringWithRequiredArg: $.Scalars["String"] & { -$: { -string: $.Scalars["String"] -} -} | null -stringWithArgs: $.Scalars["String"] & { -$?: { -string?: $.Scalars["String"] | null -int?: $.Scalars["Int"] | null -float?: $.Scalars["Float"] | null -boolean?: $.Scalars["Boolean"] | null -id?: $.Scalars["ID"] | null -} -} | null -object: Object.Object | null -objectWithArgs: Object.Object & { -$?: { -string?: $.Scalars["String"] | null -int?: $.Scalars["Int"] | null -float?: $.Scalars["Float"] | null -boolean?: $.Scalars["Boolean"] | null -id?: $.Scalars["ID"] | null -} -} | null -fooBarUnion: Union.FooBarUnion | null -/** -* Query enum field documentation. -*/ -abcEnum: Enum.ABCEnum | null -} + export interface Query { + __typename: { + type: 'Query' + nullable: false + args: null + } + string: { + type: $.Scalars['String'] + nullable: true + args: null + } + stringWithRequiredArg: { + type: $.Scalars['String'] + nullable: true + args: { + type: { + string: $.Scalars['String'] + } + allOptional: false + } + } + stringWithArgs: { + type: $.Scalars['String'] + nullable: true + args: { + type: { + string?: $.Scalars['String'] | null + int?: $.Scalars['Int'] | null + float?: $.Scalars['Float'] | null + boolean?: $.Scalars['Boolean'] | null + id?: $.Scalars['ID'] | null + } + allOptional: true + } + } + object: { + type: Object.Object + nullable: true + args: null + } + objectWithArgs: { + type: Object.Object + nullable: true + args: { + type: { + string?: $.Scalars['String'] | null + int?: $.Scalars['Int'] | null + float?: $.Scalars['Float'] | null + boolean?: $.Scalars['Boolean'] | null + id?: $.Scalars['ID'] | null + } + allOptional: true + } + } + fooBarUnion: { + type: Union.FooBarUnion + nullable: true + args: null + } + /** + * Query enum field documentation. + */ + abcEnum: { + type: Enum.ABCEnum + nullable: true + args: null + } + } } // ------------------------------------------------------------ // @@ -66,18 +99,18 @@ abcEnum: Enum.ABCEnum | null // ------------------------------------------------------------ // export namespace Enum { -/** -* Enum documentation. -* -* Members -* "A" - (DEPRECATED: Enum value A is deprecated.) -* "B" - Enum B member documentation. -* "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) -*/ -export type ABCEnum = -| "A" -| "B" -| "C" + /** + * Enum documentation. + * + * Members + * "A" - (DEPRECATED: Enum value A is deprecated.) + * "B" - Enum B member documentation. + * "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) + */ + export type ABCEnum = + | 'A' + | 'B' + | 'C' } // ------------------------------------------------------------ // @@ -85,8 +118,7 @@ export type ABCEnum = // ------------------------------------------------------------ // export namespace InputObject { -// -- no types -- - + // -- no types -- } // ------------------------------------------------------------ // @@ -94,8 +126,7 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { -// -- no types -- - + // -- no types -- } // ------------------------------------------------------------ // @@ -103,32 +134,72 @@ export namespace Interface { // ------------------------------------------------------------ // export namespace Object { -/** -* Object documentation. -*/ -export interface Foo { -__typename: "Foo" -/** -* Field documentation. -* -* @deprecated Field a is deprecated. -*/ -a: $.Scalars["String"] | null -} - -export interface Bar { -__typename: "Bar" -b: $.Scalars["Int"] | null -} - -export interface Object { -__typename: "Object" -a: $.Scalars["String"] | null -b: $.Scalars["Int"] | null -c: $.Scalars["Float"] | null -d: $.Scalars["Boolean"] | null -e: $.Scalars["ID"] | null -} + /** + * Object documentation. + */ + export interface Foo { + __typename: { + type: 'Foo' + nullable: false + args: null + } + /** + * Field documentation. + * + * @deprecated Field a is deprecated. + */ + a: { + type: $.Scalars['String'] + nullable: true + args: null + } + } + + export interface Bar { + __typename: { + type: 'Bar' + nullable: false + args: null + } + b: { + type: $.Scalars['Int'] + nullable: true + args: null + } + } + + export interface Object { + __typename: { + type: 'Object' + nullable: false + args: null + } + a: { + type: $.Scalars['String'] + nullable: true + args: null + } + b: { + type: $.Scalars['Int'] + nullable: true + args: null + } + c: { + type: $.Scalars['Float'] + nullable: true + args: null + } + d: { + type: $.Scalars['Boolean'] + nullable: true + args: null + } + e: { + type: $.Scalars['ID'] + nullable: true + args: null + } + } } // ------------------------------------------------------------ // @@ -136,10 +207,10 @@ e: $.Scalars["ID"] | null // ------------------------------------------------------------ // export namespace Union { -/** -* Union documentation. -*/ -export type FooBarUnion = -| Object.Foo& { $$union:true} -| Object.Bar& { $$union:true} -} \ No newline at end of file + /** + * Union documentation. + */ + export type FooBarUnion = + | Object.Foo & { $$union: true } + | Object.Bar & { $$union: true } +} diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index aa82150e1..9818bfef8 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -30,37 +30,72 @@ ID: string export namespace Root { export interface Query { -__typename: "Query" -string: $.Scalars["String"] | null -stringWithRequiredArg: $.Scalars["String"] & { -$: { +__typename: { +type: "Query" +nullable: false +args: null +} +string: { +type: $.Scalars["String"] +nullable: true +args: null +} +stringWithRequiredArg: { +type: $.Scalars["String"] +nullable: true +args: { +type: { string: $.Scalars["String"] } -} | null -stringWithArgs: $.Scalars["String"] & { -$?: { +allOptional: false +} +} +stringWithArgs: { +type: $.Scalars["String"] +nullable: true +args: { +type: { string?: $.Scalars["String"] | null int?: $.Scalars["Int"] | null float?: $.Scalars["Float"] | null boolean?: $.Scalars["Boolean"] | null id?: $.Scalars["ID"] | null } -} | null -object: Object.Object | null -objectWithArgs: Object.Object & { -$?: { +allOptional: true +} +} +object: { +type: Object.Object +nullable: true +args: null +} +objectWithArgs: { +type: Object.Object +nullable: true +args: { +type: { string?: $.Scalars["String"] | null int?: $.Scalars["Int"] | null float?: $.Scalars["Float"] | null boolean?: $.Scalars["Boolean"] | null id?: $.Scalars["ID"] | null } -} | null -fooBarUnion: Union.FooBarUnion | null +allOptional: true +} +} +fooBarUnion: { +type: Union.FooBarUnion +nullable: true +args: null +} /** * Query enum field documentation. */ -abcEnum: Enum.ABCEnum | null +abcEnum: { +type: Enum.ABCEnum +nullable: true +args: null +} } } @@ -110,27 +145,67 @@ export namespace Object { * Object documentation. */ export interface Foo { -__typename: "Foo" +__typename: { +type: "Foo" +nullable: false +args: null +} /** * Field documentation. * * @deprecated Field a is deprecated. */ -a: $.Scalars["String"] | null +a: { +type: $.Scalars["String"] +nullable: true +args: null +} } export interface Bar { -__typename: "Bar" -b: $.Scalars["Int"] | null +__typename: { +type: "Bar" +nullable: false +args: null +} +b: { +type: $.Scalars["Int"] +nullable: true +args: null +} } export interface Object { -__typename: "Object" -a: $.Scalars["String"] | null -b: $.Scalars["Int"] | null -c: $.Scalars["Float"] | null -d: $.Scalars["Boolean"] | null -e: $.Scalars["ID"] | null +__typename: { +type: "Object" +nullable: false +args: null +} +a: { +type: $.Scalars["String"] +nullable: true +args: null +} +b: { +type: $.Scalars["Int"] +nullable: true +args: null +} +c: { +type: $.Scalars["Float"] +nullable: true +args: null +} +d: { +type: $.Scalars["Boolean"] +nullable: true +args: null +} +e: { +type: $.Scalars["ID"] +nullable: true +args: null +} } } diff --git a/tests/builder/generate.test.ts b/tests/builder/generate.test.ts index 36a8d714e..1fb013c2c 100644 --- a/tests/builder/generate.test.ts +++ b/tests/builder/generate.test.ts @@ -1,11 +1,13 @@ import { readFile } from 'fs/promises' import { expect, test } from 'vitest' -import { generateFile } from '../../src/lib/generate.js' +import { generateFile } from '../../src/generator/generator.js' test(`generates types from GraphQL SDL file`, async () => { await generateFile({ schemaPath: `./tests/builder/_/schema.graphql`, typeScriptPath: `./tests/builder/_/schema.ts`, }) - expect(await readFile(`./tests/builder/_/schema.ts`, `utf8`)).toMatchSnapshot() + expect( + await readFile(`./tests/builder/_/schema.ts`, `utf8`), + ).toMatchSnapshot() }) From 3fe8d8dcbab8ee16799a9614c6d739f38f59dc48 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Tue, 20 Feb 2024 23:32:32 -0500 Subject: [PATCH 19/71] top level --- src/{lib => }/ResultSet/ResultSet.test-d.ts | 2 +- src/{lib => }/ResultSet/ResultSet.ts | 2 +- src/ResultSet/ResultSetReference.ts | 183 ++++++++++++++++++ src/{lib => }/ResultSet/_ResultSet.ts | 0 .../SelectionSet/SelectionSet.test-d.ts | 2 +- src/{lib => }/SelectionSet/SelectionSet.ts | 4 +- src/{lib => }/SelectionSet/_SelectionSet.ts | 0 src/lib/ResultSetReference.ts | 183 ------------------ src/lib/client.ts | 7 +- src/{lib => }/schema/Schema.ts | 0 src/{lib => }/schema/_Schema.ts | 0 11 files changed, 192 insertions(+), 191 deletions(-) rename src/{lib => }/ResultSet/ResultSet.test-d.ts (99%) rename src/{lib => }/ResultSet/ResultSet.ts (98%) create mode 100644 src/ResultSet/ResultSetReference.ts rename src/{lib => }/ResultSet/_ResultSet.ts (100%) rename src/{lib => }/SelectionSet/SelectionSet.test-d.ts (98%) rename src/{lib => }/SelectionSet/SelectionSet.ts (97%) rename src/{lib => }/SelectionSet/_SelectionSet.ts (100%) delete mode 100644 src/lib/ResultSetReference.ts rename src/{lib => }/schema/Schema.ts (100%) rename src/{lib => }/schema/_Schema.ts (100%) diff --git a/src/lib/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts similarity index 99% rename from src/lib/ResultSet/ResultSet.test-d.ts rename to src/ResultSet/ResultSet.test-d.ts index d3cf15b16..d5ac8e42d 100644 --- a/src/lib/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -1,5 +1,5 @@ import { expectTypeOf, test } from 'vitest' -import type * as Schema from '../../../tests/builder/_/schema.js' +import type * as Schema from '../../tests/builder/_/schema.js' import type { ResultSet } from './_ResultSet.js' type I = Schema.$.Index diff --git a/src/lib/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts similarity index 98% rename from src/lib/ResultSet/ResultSet.ts rename to src/ResultSet/ResultSet.ts index 53bdf4dae..24ee7477a 100644 --- a/src/lib/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/ban-types */ +import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/_Schema.js' import type * as SelectionSet from '../SelectionSet/SelectionSet.js' -import type { TSError } from '../TSError.js' export type Query<$SelectionSetQuery extends object, $Index extends Schema.Index> = Object< $SelectionSetQuery, diff --git a/src/ResultSet/ResultSetReference.ts b/src/ResultSet/ResultSetReference.ts new file mode 100644 index 000000000..ee796842d --- /dev/null +++ b/src/ResultSet/ResultSetReference.ts @@ -0,0 +1,183 @@ +// /* eslint-disable @typescript-eslint/ban-types */ + +// /** +// * The follow types work around limitations in the GenQL types with regards to union types. +// * @see https://github.com/remorses/genql/issues/108 +// */ +// import type { UnionToIntersection } from 'type-fest' +// import type { ConditionalSimplifyDeep } from 'type-fest/source/conditional-simplify' +// import type { Values } from '~/lib/prelude' +// import { type TSError, type ValuesOrEmptyObject } from '~/lib/prelude' +// import type * as GenqlTypes from '../genql/schema.js' +// import type { FieldNameConstants, Interface, List, Node, Nullable, Objekt, Scalar } from '../types' +// import type { GetSelection } from './utilities.js' + +// type SimplifyDeep = ConditionalSimplifyDeep | Date, object> // eslint-disable-line + +// type SelectionRoot = SelectionObjekt +// type SelectionScalar = boolean +// type SelectionObjekt = object + +// export type SelectMutation<$SelectionRoot extends SelectionRoot> = Select<'Mutation', $SelectionRoot> + +// export type SelectQuery<$SelectionRoot extends SelectionRoot> = Select<'Query', $SelectionRoot> + +// export type Selection< +// $TypeName extends keyof GenqlTypes.Selections, +// $SelectionObjekt extends GetSelection<$TypeName>, +// > = $SelectionObjekt + +// export type Select< +// $TypeName extends keyof GenqlTypes.Selections, +// $SelectionObjekt extends GetSelection<$TypeName>, +// > // @ts-expect-error fixme +// = SimplifyDeep> + +// export type SelectOn< +// $Node extends Objekt, +// $SelectionObjekt extends GetSelection<$Node['__typename']>, +// > = SimplifyDeep> + +// // dprint-ignore +// type SelectObjekt<$Node extends Node, $Selection> = +// $Selection extends object ? $Node extends Nullable ? null | SelectObjekt, $Selection> +// : $Node extends Scalar ? TSError<'SelectObjekt','$Node is Scalar (should be Object).'> +// : $Node extends List ? TSError<'SelectObjekt','$Node is List (should be Object).'> +// : $Node extends List ? SelectObjekt_<$Node[number], $Selection>[] +// // : $Node extends Interface ? SelectInterface<$Node, $Selection> +// : $Node extends Objekt ? SelectObjekt_<$Node, $Selection> +// : TSError<'SelectObjekt','$Node is unknown type (should be Objekt).', { $Node: $Node }> +// : TSError<'SelectObjekt','$Selection is not an object.', { $Selection: $Selection }> + +// // dprint-ignore +// type SelectObjekt_<$Node extends Objekt, $Selection extends SelectionObjekt> = + +// /** +// * Nothing to do, the selection set is empty (e.g.: \{\} ) +// */ +// keyof $Selection extends never ? {} +// : +// /** +// * Handle "regular" selection set fields (not a union or interface) +// */ +// { +// [ +// $Field in keyof $Selection & string as +// $Field extends FieldNameConstants.__scalar ? never +// : $Field extends FieldNameConstants.__args ? never +// : $Field extends `on_${infer _}` ? never +// : $Selection[$Field] extends false ? never +// : $Field +// ]: +// SelectObjektField<$Node, $Selection, $Field> +// } + +// & + +// /** +// * Handle the __scalar: true feature. This special field selector means +// * select _all_ scalar fields of the object. +// */ +// ( +// IsSelectsField extends true +// ? { +// [$Field in keyof $Node & string as $Node[$Field] extends Scalar | Nullable ? $Field : never]: $Node[$Field] +// } +// : {} // eslint-disable-line +// ) + +// & + +// /** +// * Handle selection sets for interfaces and union type members. +// * +// * @remarks This selection set needs to be "hoisted" into the parent selection set. +// * Hence the TypeScript intersection ("&") above. +// */ +// UnionToIntersection +// : $InterfaceOrObjectTypeName extends keyof GenqlTypes.Objects ? SelectObjekt +// : TSError<'SelectObjekt_', `The given type name in on_TYPE_NAME of ${$InterfaceOrObjectTypeName} does not correspond to any know object or interface name.`> +// : never +// }>> + +// & + +// ( +// $Node extends Interface ? +// // FieldNameConstants.__typenameInterface extends keyof $Node ? +// // $Node[FieldNameConstants.__typenameInterface] extends keyof GenqlTypes.Interfaces.$Guide ? +// // Values extends Objekt ? +// // @ts-expect-error fixme +// SelectAgainstInterface, $Selection> +// : {} +// // : never +// // : never +// // : never +// ) + +// // dprint-ignore +// type SelectAgainstInterface< +// $Implementor extends Objekt, +// $Selection, +// > = +// $Implementor extends any ? // force distribution over union type +// `on_${$Implementor['__typename']}` extends keyof $Selection +// ? SelectObjekt<$Implementor, $Selection[`on_${$Implementor['__typename']}`]> +// : {} +// : never + +// // dprint-ignore +// type SelectObjektField<$Objekt extends Objekt, $SelectionObjekt extends SelectionObjekt, $Field extends keyof $SelectionObjekt & string> = +// $Field extends keyof $Objekt ? $Objekt[$Field] extends Node ? $SelectionObjekt[$Field] extends SelectionScalar ? SelectScalar<$Objekt[$Field], $SelectionObjekt[$Field]> +// : $SelectionObjekt[$Field] extends SelectionObjekt ? SelectObjekt<$Objekt[$Field], $SelectionObjekt[$Field]> +// : TSError<'SelectionObjektField', `selection object field "${$Field}" is of unknown type.`> +// : [$SelectionObjekt,$Field,$Objekt[$Field],TSError<'SelectObjektField', `object selection field "${$Field}" on schema object "${$Objekt['__typename']}" is not a Node.`>] +// // : $Field extends `on_${infer $TypeName}` ? TSError<'SelectObjektField', `fragment field "on_${$TypeName}" should be handled by caller.`> +// : TSError<'SelectObjektField', `object selection field "${$Field}" does not exist on schema object "${$Objekt['__typename']}"`> + +// // dprint-ignore +// type SelectScalar<$Node extends Node, $SelectionScalar extends SelectionScalar> = +// $Node extends Nullable ? null | SelectScalar, $SelectionScalar> +// : $Node extends List ? SelectScalar<$Node[number], $SelectionScalar>[] +// : $Node extends Objekt ? TSError<'SelectScalar','$Node is Objekt.'> +// : $Node extends Scalar ? $SelectionScalar extends true ? $Node +// : never +// : TSError<'SelectScalar','$Node is of unknown type.'> + +// type IsSelectsField< +// $Field extends string, +// $SelectionObjekt extends SelectionObjekt, +// > = $Field extends keyof $SelectionObjekt ? ($SelectionObjekt[$Field] extends true ? true : false) : false + +// // // dprint-ignore +// // type GetInterfaceImplementorSelections<$Interface extends Interface, $Selection> = +// // $Selection[GetInterfaceImplementorSelections_<$Interface, keyof $Selection & string>] +// // // dprint-ignore +// // type GetInterfaceImplementorSelections_<$Interface extends Interface, $SelectionField extends string> = +// // $SelectionField extends `on_${infer _ extends keyof GenqlTypes.Interfaces.$Guide[$Interface[FieldNameConstants.__typenameInterface]]['implementors'] & string}` +// // ? $SelectionField +// // : never diff --git a/src/lib/ResultSet/_ResultSet.ts b/src/ResultSet/_ResultSet.ts similarity index 100% rename from src/lib/ResultSet/_ResultSet.ts rename to src/ResultSet/_ResultSet.ts diff --git a/src/lib/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts similarity index 98% rename from src/lib/SelectionSet/SelectionSet.test-d.ts rename to src/SelectionSet/SelectionSet.test-d.ts index 6c2b2e55f..97a5748bb 100644 --- a/src/lib/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -1,5 +1,5 @@ import { assertType, test } from 'vitest' -import type * as Schema from '../../../tests/builder/_/schema.js' +import type * as Schema from '../../tests/builder/_/schema.js' import type { Query } from './SelectionSet.js' type S = Query diff --git a/src/lib/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts similarity index 97% rename from src/lib/SelectionSet/SelectionSet.ts rename to src/SelectionSet/SelectionSet.ts index 528616999..40ae31f1e 100644 --- a/src/lib/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/ban-types */ -import type { MaybeList, NonEmptyString } from '../prelude.js' +import type { MaybeList, NonEmptyString } from '../lib/prelude.js' +import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/_Schema.js' -import type { TSError } from '../TSError.js' export type OmitArgs = Omit diff --git a/src/lib/SelectionSet/_SelectionSet.ts b/src/SelectionSet/_SelectionSet.ts similarity index 100% rename from src/lib/SelectionSet/_SelectionSet.ts rename to src/SelectionSet/_SelectionSet.ts diff --git a/src/lib/ResultSetReference.ts b/src/lib/ResultSetReference.ts deleted file mode 100644 index cfe0bac45..000000000 --- a/src/lib/ResultSetReference.ts +++ /dev/null @@ -1,183 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-types */ - -/** - * The follow types work around limitations in the GenQL types with regards to union types. - * @see https://github.com/remorses/genql/issues/108 - */ -import type { UnionToIntersection } from 'type-fest' -import type { ConditionalSimplifyDeep } from 'type-fest/source/conditional-simplify' -import type { Values } from '~/lib/prelude' -import { type TSError, type ValuesOrEmptyObject } from '~/lib/prelude' -import type * as GenqlTypes from '../genql/schema.js' -import type { FieldNameConstants, Interface, List, Node, Nullable, Objekt, Scalar } from '../types' -import type { GetSelection } from './utilities.js' - -type SimplifyDeep = ConditionalSimplifyDeep | Date, object> // eslint-disable-line - -type SelectionRoot = SelectionObjekt -type SelectionScalar = boolean -type SelectionObjekt = object - -export type SelectMutation<$SelectionRoot extends SelectionRoot> = Select<'Mutation', $SelectionRoot> - -export type SelectQuery<$SelectionRoot extends SelectionRoot> = Select<'Query', $SelectionRoot> - -export type Selection< - $TypeName extends keyof GenqlTypes.Selections, - $SelectionObjekt extends GetSelection<$TypeName>, -> = $SelectionObjekt - -export type Select< - $TypeName extends keyof GenqlTypes.Selections, - $SelectionObjekt extends GetSelection<$TypeName>, -> // @ts-expect-error fixme - = SimplifyDeep> - -export type SelectOn< - $Node extends Objekt, - $SelectionObjekt extends GetSelection<$Node['__typename']>, -> = SimplifyDeep> - -// dprint-ignore -type SelectObjekt<$Node extends Node, $Selection> = - $Selection extends object ? $Node extends Nullable ? null | SelectObjekt, $Selection> - : $Node extends Scalar ? TSError<'SelectObjekt','$Node is Scalar (should be Object).'> - : $Node extends List ? TSError<'SelectObjekt','$Node is List (should be Object).'> - : $Node extends List ? SelectObjekt_<$Node[number], $Selection>[] - // : $Node extends Interface ? SelectInterface<$Node, $Selection> - : $Node extends Objekt ? SelectObjekt_<$Node, $Selection> - : TSError<'SelectObjekt','$Node is unknown type (should be Objekt).', { $Node: $Node }> - : TSError<'SelectObjekt','$Selection is not an object.', { $Selection: $Selection }> - -// dprint-ignore -type SelectObjekt_<$Node extends Objekt, $Selection extends SelectionObjekt> = - - /** - * Nothing to do, the selection set is empty (e.g.: \{\} ) - */ - keyof $Selection extends never ? {} - : - /** - * Handle "regular" selection set fields (not a union or interface) - */ - { - [ - $Field in keyof $Selection & string as - $Field extends FieldNameConstants.__scalar ? never - : $Field extends FieldNameConstants.__args ? never - : $Field extends `on_${infer _}` ? never - : $Selection[$Field] extends false ? never - : $Field - ]: - SelectObjektField<$Node, $Selection, $Field> - } - - & - - /** - * Handle the __scalar: true feature. This special field selector means - * select _all_ scalar fields of the object. - */ - ( - IsSelectsField extends true - ? { - [$Field in keyof $Node & string as $Node[$Field] extends Scalar | Nullable ? $Field : never]: $Node[$Field] - } - : {} // eslint-disable-line - ) - - & - - /** - * Handle selection sets for interfaces and union type members. - * - * @remarks This selection set needs to be "hoisted" into the parent selection set. - * Hence the TypeScript intersection ("&") above. - */ - UnionToIntersection - : $InterfaceOrObjectTypeName extends keyof GenqlTypes.Objects ? SelectObjekt - : TSError<'SelectObjekt_', `The given type name in on_TYPE_NAME of ${$InterfaceOrObjectTypeName} does not correspond to any know object or interface name.`> - : never - }>> - - & - - ( - $Node extends Interface ? - // FieldNameConstants.__typenameInterface extends keyof $Node ? - // $Node[FieldNameConstants.__typenameInterface] extends keyof GenqlTypes.Interfaces.$Guide ? - // Values extends Objekt ? - // @ts-expect-error fixme - SelectAgainstInterface, $Selection> - : {} - // : never - // : never - // : never - ) - -// dprint-ignore -type SelectAgainstInterface< - $Implementor extends Objekt, - $Selection, -> = -$Implementor extends any ? // force distribution over union type - `on_${$Implementor['__typename']}` extends keyof $Selection - ? SelectObjekt<$Implementor, $Selection[`on_${$Implementor['__typename']}`]> - : {} -: never - -// dprint-ignore -type SelectObjektField<$Objekt extends Objekt, $SelectionObjekt extends SelectionObjekt, $Field extends keyof $SelectionObjekt & string> = - $Field extends keyof $Objekt ? $Objekt[$Field] extends Node ? $SelectionObjekt[$Field] extends SelectionScalar ? SelectScalar<$Objekt[$Field], $SelectionObjekt[$Field]> - : $SelectionObjekt[$Field] extends SelectionObjekt ? SelectObjekt<$Objekt[$Field], $SelectionObjekt[$Field]> - : TSError<'SelectionObjektField', `selection object field "${$Field}" is of unknown type.`> - : [$SelectionObjekt,$Field,$Objekt[$Field],TSError<'SelectObjektField', `object selection field "${$Field}" on schema object "${$Objekt['__typename']}" is not a Node.`>] - // : $Field extends `on_${infer $TypeName}` ? TSError<'SelectObjektField', `fragment field "on_${$TypeName}" should be handled by caller.`> - : TSError<'SelectObjektField', `object selection field "${$Field}" does not exist on schema object "${$Objekt['__typename']}"`> - -// dprint-ignore -type SelectScalar<$Node extends Node, $SelectionScalar extends SelectionScalar> = - $Node extends Nullable ? null | SelectScalar, $SelectionScalar> - : $Node extends List ? SelectScalar<$Node[number], $SelectionScalar>[] - : $Node extends Objekt ? TSError<'SelectScalar','$Node is Objekt.'> - : $Node extends Scalar ? $SelectionScalar extends true ? $Node - : never - : TSError<'SelectScalar','$Node is of unknown type.'> - -type IsSelectsField< - $Field extends string, - $SelectionObjekt extends SelectionObjekt, -> = $Field extends keyof $SelectionObjekt ? ($SelectionObjekt[$Field] extends true ? true : false) : false - -// // dprint-ignore -// type GetInterfaceImplementorSelections<$Interface extends Interface, $Selection> = -// $Selection[GetInterfaceImplementorSelections_<$Interface, keyof $Selection & string>] -// // dprint-ignore -// type GetInterfaceImplementorSelections_<$Interface extends Interface, $SelectionField extends string> = -// $SelectionField extends `on_${infer _ extends keyof GenqlTypes.Interfaces.$Guide[$Interface[FieldNameConstants.__typenameInterface]]['implementors'] & string}` -// ? $SelectionField -// : never diff --git a/src/lib/client.ts b/src/lib/client.ts index 8c60b4349..3cf1ee915 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -1,6 +1,7 @@ -import type { Query } from './ResultSet/ResultSet.js' -import type { Index } from './schema/Schema.js' -import type { SelectionSet } from './SelectionSet/_SelectionSet.js' +import { ResultSet } from '../ResultSet/_ResultSet.js' +import type { Query } from '../ResultSet/ResultSet.js' +import type { Index } from '../schema/Schema.js' +import type { SelectionSet } from '../SelectionSet/_SelectionSet.js' export type Client<$SchemaIndex extends Index> = & ( diff --git a/src/lib/schema/Schema.ts b/src/schema/Schema.ts similarity index 100% rename from src/lib/schema/Schema.ts rename to src/schema/Schema.ts diff --git a/src/lib/schema/_Schema.ts b/src/schema/_Schema.ts similarity index 100% rename from src/lib/schema/_Schema.ts rename to src/schema/_Schema.ts From 94ea55bf31fafcc65e503e5932b296d83292ee10 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 23 Feb 2024 18:07:27 -0500 Subject: [PATCH 20/71] restyle rebarrel --- src/ResultSet/ResultSet.test-d.ts | 16 ++- src/ResultSet/ResultSet.ts | 32 +++-- src/ResultSet/{_ResultSet.ts => __.ts} | 0 src/SelectionSet/SelectionSet.ts | 118 +++++++++++-------- src/SelectionSet/{_SelectionSet.ts => __.ts} | 0 src/lib/client.ts | 54 +++++---- src/schema/{_Schema.ts => __.ts} | 0 7 files changed, 126 insertions(+), 94 deletions(-) rename src/ResultSet/{_ResultSet.ts => __.ts} (100%) rename src/SelectionSet/{_SelectionSet.ts => __.ts} (100%) rename src/schema/{_Schema.ts => __.ts} (100%) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index d5ac8e42d..940bc892e 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -1,20 +1,26 @@ import { expectTypeOf, test } from 'vitest' import type * as Schema from '../../tests/builder/_/schema.js' -import type { ResultSet } from './_ResultSet.js' +import type { ResultSet } from './__.js' type I = Schema.$.Index // dprint-ignore test(`general`, () => { // scalar - expectTypeOf>().toEqualTypeOf<{ string: string | null }>() - + expectTypeOf>().toEqualTypeOf<{ + string: string | null + }>() + // Arguments // scalar - expectTypeOf>().toEqualTypeOf<{ stringWithArgs: null | string }>() + expectTypeOf>().toEqualTypeOf<{ + stringWithArgs: null | string + }>() // error: unknown field - expectTypeOf>().toEqualTypeOf<{ string2: ResultSet.Errors.UnknownFieldName<'string2', Schema.Root.Query> }>() + expectTypeOf>().toEqualTypeOf<{ + string2: ResultSet.Errors.UnknownFieldName<'string2', Schema.Root.Query> + }>() // assertType>({string:''}) }) diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index 24ee7477a..fa1e74545 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -1,13 +1,13 @@ /* eslint-disable @typescript-eslint/ban-types */ import type { TSError } from '../lib/TSError.js' -import type { Schema } from '../schema/_Schema.js' +import type { Schema } from '../schema/__.js' import type * as SelectionSet from '../SelectionSet/SelectionSet.js' -export type Query<$SelectionSetQuery extends object, $Index extends Schema.Index> = Object< - $SelectionSetQuery, - Exclude<$Index['Root']['Query'], null> -> +export type Query< + $SelectionSetQuery extends object, + $Index extends Schema.Index, +> = Object<$SelectionSetQuery, Exclude<$Index['Root']['Query'], null>> // // dprint-ignore // export type ResultSet<$SelectionSet extends object, $Node extends Schema.Node> = @@ -15,17 +15,25 @@ export type Query<$SelectionSetQuery extends object, $Index extends Schema.Index // ? null | ResultSet<$SelectionSet, Exclude<$Node, Schema.Nullable>> // : ResultSetObject<$SelectionSet, $Node> -export type Object<$SelectionSet extends object, $Object extends Schema.Object> = { - [ - $Key in keyof SelectionSet.OmitArgs<$SelectionSet> & string - ]: $Key extends keyof $Object ? Field> - : Errors.UnknownFieldName<$Key,$Object> +export type Object< + $SelectionSet extends object, + $Object extends Schema.Object, +> = { + [$Key in keyof SelectionSet.OmitArgs<$SelectionSet> & + string]: $Key extends keyof $Object + ? Field> + : Errors.UnknownFieldName<$Key, $Object> } -type Field<$Field extends Schema.Field> = $Field['type'] | ($Field['nullable'] extends true ? null : never) +type Field<$Field extends Schema.Field> = + | $Field['type'] + | ($Field['nullable'] extends true ? null : never) export namespace Errors { - export type UnknownFieldName<$FieldName extends string, $Node extends Schema.Object> = TSError< + export type UnknownFieldName< + $FieldName extends string, + $Node extends Schema.Object, + > = TSError< 'ResultSetObject', `field "${$FieldName}" does not exist on schema object "${$Node['__typename']['type']}"` > diff --git a/src/ResultSet/_ResultSet.ts b/src/ResultSet/__.ts similarity index 100% rename from src/ResultSet/_ResultSet.ts rename to src/ResultSet/__.ts diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 40ae31f1e..da62ad5b9 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -2,74 +2,86 @@ import type { MaybeList, NonEmptyString } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' -import type { Schema } from '../schema/_Schema.js' +import type { Schema } from '../schema/__.js' export type OmitArgs = Omit -export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Object - ? SelectionSetObject<$Index['Root']['Query'], $Index> - : never +export type Query<$Index extends Schema.Index> = + $Index['Root']['Query'] extends Object + ? SelectionSetObject<$Index['Root']['Query'], $Index> + : never -export type Mutation<$Index extends Schema.Index> = $Index['Root']['Mutation'] extends Object - ? SelectionSetObject<$Index['Root']['Mutation'], $Index> - : never +export type Mutation<$Index extends Schema.Index> = + $Index['Root']['Mutation'] extends Object + ? SelectionSetObject<$Index['Root']['Mutation'], $Index> + : never -export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscription'] extends Object - ? SelectionSetObject<$Index['Root']['Subscription'], $Index> - : never +export type Subscription<$Index extends Schema.Index> = + $Index['Root']['Subscription'] extends Object + ? SelectionSetObject<$Index['Root']['Subscription'], $Index> + : never -type SelectionSetObject<$Object extends Schema.Object, $Index extends Schema.Index> = - & { - [Key in keyof $Object]?: SelectionSetField, $Index> - } - /** - * Alias support. - * Allow every field to also be given as a key with this pattern `_as_: ...` - */ - & { - [Key in keyof $Object as `${keyof OmitArgs<$Object> & string}_as_${NonEmptyString}`]?: SelectionSetField< - Schema.AsField<$Object[Key]>, - $Index - > - } - & FieldDirectives - /** +type SelectionSetObject< + $Object extends Schema.Object, + $Index extends Schema.Index, +> = { + [Key in keyof $Object]?: SelectionSetField< + Schema.AsField<$Object[Key]>, + $Index + > +} & /** + * Alias support. + * Allow every field to also be given as a key with this pattern `_as_: ...` + */ +{ + [Key in keyof $Object as `${keyof OmitArgs<$Object> & + string}_as_${NonEmptyString}`]?: SelectionSetField< + Schema.AsField<$Object[Key]>, + $Index + > +} & FieldDirectives & /** * Inline fragments for field groups. * @see https://spec.graphql.org/draft/#sec-Inline-Fragments */ - & { + { ___?: MaybeList> - } - /** + } & /** * Special property to select all scalars. */ - & { $scalars?: ClientIndicator } + { $scalars?: ClientIndicator } // dprint-ignore -export type SelectionSetField<$Field extends Schema.Field, $Index extends Schema.Index> = -$Field extends Schema.ScalarField ? Indicator<$Field> : -$Field extends Schema.unionField ? SelectionSetUnion<$Field['type'], $Index> : -$Field extends Schema.ObjectField ? SelectionSetObject<$Field['type'], $Index> & Arguments<$Field> : - TSError<'SelectionSetField', '$Field case not handled', { $Field:$Field }> +export type SelectionSetField< + $Field extends Schema.Field, + $Index extends Schema.Index, +> = $Field extends Schema.ScalarField + ? Indicator<$Field> + : $Field extends Schema.unionField + ? SelectionSetUnion<$Field['type'], $Index> + : $Field extends Schema.ObjectField + ? SelectionSetObject<$Field['type'], $Index> & Arguments<$Field> + : TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.Args - ? $Field['args']['allOptional'] extends true ? { - $?: $Field['args']['type'] - } - : { - $: $Field['args']['type'] - } + ? $Field['args']['allOptional'] extends true + ? { + $?: $Field['args']['type'] + } + : { + $: $Field['args']['type'] + } : {} // TODO why does $object not get passed to this in a distributed way? -type SelectionSetUnion<$Object extends Schema.Union, $Index extends Schema.Index> = - & { - [Key in $Object['__typename']['type'] as `on${Capitalize}`]?: SelectionSetObject< - Schema.OmitUnionBrand>, - $Index - > - } - & { __typename?: NoArgsIndicator } +type SelectionSetUnion< + $Object extends Schema.Union, + $Index extends Schema.Index, +> = { + [Key in $Object['__typename']['type'] as `on${Capitalize}`]?: SelectionSetObject< + Schema.OmitUnionBrand>, + $Index + > +} & { __typename?: NoArgsIndicator } /** * Helpers @@ -90,9 +102,11 @@ export type NoArgsIndicator = ClientIndicator | FieldDirectives // dprint-ignore export type Indicator<$FieldType extends Schema.Field> = -$FieldType['args'] extends Schema.Args ? $FieldType['args']['allOptional'] extends true ? ({ $?: $FieldType['args']['type'] } & FieldDirectives) | ClientIndicator - : ({ $: $FieldType['args']['type'] } & FieldDirectives) - : NoArgsIndicator + $FieldType['args'] extends Schema.Args + ? $FieldType['args']['allOptional'] extends true + ? ({ $?: $FieldType['args']['type'] } & FieldDirectives) | ClientIndicator + : { $: $FieldType['args']['type'] } & FieldDirectives + : NoArgsIndicator /** * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives diff --git a/src/SelectionSet/_SelectionSet.ts b/src/SelectionSet/__.ts similarity index 100% rename from src/SelectionSet/_SelectionSet.ts rename to src/SelectionSet/__.ts diff --git a/src/lib/client.ts b/src/lib/client.ts index 3cf1ee915..f933f0c7d 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -1,38 +1,42 @@ -import { ResultSet } from '../ResultSet/_ResultSet.js' +import type { ResultSet } from '../ResultSet/__.js' import type { Query } from '../ResultSet/ResultSet.js' import type { Index } from '../schema/Schema.js' -import type { SelectionSet } from '../SelectionSet/_SelectionSet.js' +import type { SelectionSet } from '../SelectionSet/__.js' export type Client<$SchemaIndex extends Index> = - & ( - $SchemaIndex['Root']['Query'] extends null ? { + ($SchemaIndex['Root']['Query'] extends null + ? { // dprint-ignore - query: <$SelectionSet extends SelectionSet.Query<$SchemaIndex>> - (selectionSet: $SelectionSet) => Promise> + query: <$SelectionSet extends SelectionSet.Query<$SchemaIndex>>( + selectionSet: $SelectionSet, + ) => Promise> } - : unknown - ) - & ( - $SchemaIndex['Root']['Mutation'] extends null ? { - // dprint-ignore - mutation: <$SelectionSet extends SelectionSet.Mutation<$SchemaIndex>> - (selectionSet: $SelectionSet) => Promise> - } - : unknown - ) - & ( - $SchemaIndex['Root']['Subscription'] extends null ? { - // dprint-ignore - subscription: <$SelectionSet extends SelectionSet.Subscription<$SchemaIndex>> - (selectionSet: $SelectionSet) => Promise> - } - : unknown - ) + : unknown) & + ($SchemaIndex['Root']['Mutation'] extends null + ? { + // dprint-ignore + mutation: <$SelectionSet extends SelectionSet.Mutation<$SchemaIndex>>( + selectionSet: $SelectionSet, + ) => Promise> + } + : unknown) & + ($SchemaIndex['Root']['Subscription'] extends null + ? { + // dprint-ignore + subscription: < + $SelectionSet extends SelectionSet.Subscription<$SchemaIndex>, + >( + selectionSet: $SelectionSet, + ) => Promise> + } + : unknown) interface Input { url: URL | string } -export const create = <$SchemaIndex extends Index>(input: Input): Client<$SchemaIndex> => { +export const create = <$SchemaIndex extends Index>( + input: Input, +): Client<$SchemaIndex> => { return 1 as any } diff --git a/src/schema/_Schema.ts b/src/schema/__.ts similarity index 100% rename from src/schema/_Schema.ts rename to src/schema/__.ts From f1f2d374f40df47abdce5070bccbcea43715005b Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 23 Feb 2024 19:04:24 -0500 Subject: [PATCH 21/71] work --- .gitignore | 1 + .prettierignore | 1 + package.json | 15 +- pnpm-lock.yaml | 298 +++++++------- src/ResultSet/ResultSet.test-d.ts | 24 +- src/ResultSet/ResultSet.ts | 61 +-- src/SelectionSet/SelectionSet.test-d.ts | 44 ++- src/SelectionSet/SelectionSet.ts | 95 +++-- src/lib/client.ts | 40 +- tests/builder/_/schema.graphql | 13 +- tests/builder/_/schema.ts | 363 +++++++++--------- .../__snapshots__/generate.test.ts.snap | 29 +- 12 files changed, 516 insertions(+), 468 deletions(-) diff --git a/.gitignore b/.gitignore index 5637f627a..a2483e00e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build .DS_Store *.log coverage +tsconfig.vitest-temp.json diff --git a/.prettierignore b/.prettierignore index 9e74d29e1..8810254eb 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ build pnpm-lock.yaml coverage +tsconfig.vitest-temp.json diff --git a/package.json b/package.json index 0f130d1b3..caac92d06 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "build": "pnpm clean && pnpm tsc --project tsconfig.build.json", "clean": "tsc --build --clean && rm -rf build", "test": "vitest", + "test:types": "vitest --typecheck", "test:coverage": "pnpm test -- --coverage", "release:stable": "dripip stable", "release:preview": "dripip preview", @@ -67,14 +68,14 @@ "@types/body-parser": "^1.19.5", "@types/express": "^4.17.21", "@types/json-bigint": "^1.0.4", - "@types/node": "^20.11.19", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", + "@types/node": "^20.11.20", + "@typescript-eslint/eslint-plugin": "^7.0.2", + "@typescript-eslint/parser": "^7.0.2", "apollo-server-express": "^3.13.0", "body-parser": "^1.20.2", "doctoc": "^2.2.1", "dripip": "^0.10.0", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-config-prisma": "^0.2.0", "eslint-plugin-deprecation": "^2.0.0", "eslint-plugin-only-warn": "^1.1.0", @@ -85,11 +86,11 @@ "get-port": "^7.0.0", "graphql": "^16.8.1", "graphql-tag": "^2.12.6", - "happy-dom": "^13.3.8", + "happy-dom": "^13.5.0", "json-bigint": "^1.0.0", "tsx": "^4.7.1", - "type-fest": "^4.10.2", + "type-fest": "^4.10.3", "typescript": "^5.3.3", - "vitest": "^1.3.0" + "vitest": "^1.3.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index abefb5592..4d0c08992 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,14 +29,14 @@ devDependencies: specifier: ^1.0.4 version: 1.0.4 '@types/node': - specifier: ^20.11.19 - version: 20.11.19 + specifier: ^20.11.20 + version: 20.11.20 '@typescript-eslint/eslint-plugin': - specifier: ^7.0.1 - version: 7.0.1(@typescript-eslint/parser@7.0.1)(eslint@8.56.0)(typescript@5.3.3) + specifier: ^7.0.2 + version: 7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: ^7.0.1 - version: 7.0.1(eslint@8.56.0)(typescript@5.3.3) + specifier: ^7.0.2 + version: 7.0.2(eslint@8.57.0)(typescript@5.3.3) apollo-server-express: specifier: ^3.13.0 version: 3.13.0(express@4.18.2)(graphql@16.8.1) @@ -50,23 +50,23 @@ devDependencies: specifier: ^0.10.0 version: 0.10.0 eslint: - specifier: ^8.56.0 - version: 8.56.0 + specifier: ^8.57.0 + version: 8.57.0 eslint-config-prisma: specifier: ^0.2.0 - version: 0.2.0(@typescript-eslint/eslint-plugin@7.0.1)(@typescript-eslint/parser@7.0.1)(eslint-plugin-deprecation@2.0.0)(eslint-plugin-only-warn@1.1.0)(eslint-plugin-prefer-arrow@1.2.3)(eslint-plugin-simple-import-sort@12.0.0)(eslint-plugin-tsdoc@0.2.17)(eslint@8.56.0) + version: 0.2.0(@typescript-eslint/eslint-plugin@7.0.2)(@typescript-eslint/parser@7.0.2)(eslint-plugin-deprecation@2.0.0)(eslint-plugin-only-warn@1.1.0)(eslint-plugin-prefer-arrow@1.2.3)(eslint-plugin-simple-import-sort@12.0.0)(eslint-plugin-tsdoc@0.2.17)(eslint@8.57.0) eslint-plugin-deprecation: specifier: ^2.0.0 - version: 2.0.0(eslint@8.56.0)(typescript@5.3.3) + version: 2.0.0(eslint@8.57.0)(typescript@5.3.3) eslint-plugin-only-warn: specifier: ^1.1.0 version: 1.1.0 eslint-plugin-prefer-arrow: specifier: ^1.2.3 - version: 1.2.3(eslint@8.56.0) + version: 1.2.3(eslint@8.57.0) eslint-plugin-simple-import-sort: specifier: ^12.0.0 - version: 12.0.0(eslint@8.56.0) + version: 12.0.0(eslint@8.57.0) eslint-plugin-tsdoc: specifier: ^0.2.17 version: 0.2.17 @@ -83,8 +83,8 @@ devDependencies: specifier: ^2.12.6 version: 2.12.6(graphql@16.8.1) happy-dom: - specifier: ^13.3.8 - version: 13.3.8 + specifier: ^13.5.0 + version: 13.5.0 json-bigint: specifier: ^1.0.0 version: 1.0.0 @@ -92,14 +92,14 @@ devDependencies: specifier: ^4.7.1 version: 4.7.1 type-fest: - specifier: ^4.10.2 - version: 4.10.2 + specifier: ^4.10.3 + version: 4.10.3 typescript: specifier: ^5.3.3 version: 5.3.3 vitest: - specifier: ^1.3.0 - version: 1.3.0(@types/node@20.11.19)(happy-dom@13.3.8) + specifier: ^1.3.1 + version: 1.3.1(@types/node@20.11.20)(happy-dom@13.5.0) packages: @@ -507,13 +507,13 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.56.0 + eslint: 8.57.0 eslint-visitor-keys: 3.4.3 dev: true @@ -539,8 +539,8 @@ packages: - supports-color dev: true - /@eslint/js@8.56.0: - resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -1147,27 +1147,27 @@ packages: /@types/accepts@1.3.7: resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} dependencies: - '@types/node': 20.11.19 + '@types/node': 20.11.20 dev: true /@types/body-parser@1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.38 - '@types/node': 20.11.19 + '@types/node': 20.11.20 dev: true /@types/body-parser@1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.38 - '@types/node': 20.11.19 + '@types/node': 20.11.20 dev: true /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 20.11.19 + '@types/node': 20.11.20 dev: true /@types/cors@2.8.12: @@ -1187,7 +1187,7 @@ packages: /@types/express-serve-static-core@4.17.31: resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} dependencies: - '@types/node': 20.11.19 + '@types/node': 20.11.20 '@types/qs': 6.9.11 '@types/range-parser': 1.2.7 dev: true @@ -1195,7 +1195,7 @@ packages: /@types/express-serve-static-core@4.17.43: resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==} dependencies: - '@types/node': 20.11.19 + '@types/node': 20.11.20 '@types/qs': 6.9.11 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -1257,8 +1257,8 @@ packages: resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} dev: true - /@types/node@20.11.19: - resolution: {integrity: sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==} + /@types/node@20.11.20: + resolution: {integrity: sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==} dependencies: undici-types: 5.26.5 dev: true @@ -1270,7 +1270,7 @@ packages: /@types/parse-github-url@1.0.3: resolution: {integrity: sha512-7sTbCVmSVzK/iAsHGIxoqiyAnqix9opZm68lOvaU6DBx9EQ9kHMSp0y7Criu2OCsZ9wDllEyCRU+LU4hPRxXUA==} dependencies: - '@types/node': 20.11.19 + '@types/node': 20.11.20 dev: true /@types/qs@6.9.11: @@ -1289,7 +1289,7 @@ packages: resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} dependencies: '@types/mime': 1.3.5 - '@types/node': 20.11.19 + '@types/node': 20.11.20 dev: true /@types/serve-static@1.15.5: @@ -1297,15 +1297,15 @@ packages: dependencies: '@types/http-errors': 2.0.4 '@types/mime': 3.0.4 - '@types/node': 20.11.19 + '@types/node': 20.11.20 dev: true /@types/unist@2.0.10: resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} dev: true - /@typescript-eslint/eslint-plugin@7.0.1(@typescript-eslint/parser@7.0.1)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-OLvgeBv3vXlnnJGIAgCLYKjgMEU+wBGj07MQ/nxAaON+3mLzX7mJbhRYrVGiVvFiXtwFlkcBa/TtmglHy0UbzQ==} + /@typescript-eslint/eslint-plugin@7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 @@ -1316,13 +1316,13 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.0.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 7.0.1 - '@typescript-eslint/type-utils': 7.0.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 7.0.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 7.0.1 + '@typescript-eslint/parser': 7.0.2(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 7.0.2 + '@typescript-eslint/type-utils': 7.0.2(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/utils': 7.0.2(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 7.0.2 debug: 4.3.4 - eslint: 8.56.0 + eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -1333,8 +1333,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@7.0.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-8GcRRZNzaHxKzBPU3tKtFNing571/GwPBeCvmAUw0yBtfE2XVd0zFKJIMSWkHJcPQi0ekxjIts6L/rrZq5cxGQ==} + /@typescript-eslint/parser@7.0.2(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^8.56.0 @@ -1343,12 +1343,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 7.0.1 - '@typescript-eslint/types': 7.0.1 - '@typescript-eslint/typescript-estree': 7.0.1(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 7.0.1 + '@typescript-eslint/scope-manager': 7.0.2 + '@typescript-eslint/types': 7.0.2 + '@typescript-eslint/typescript-estree': 7.0.2(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 7.0.2 debug: 4.3.4 - eslint: 8.56.0 + eslint: 8.57.0 typescript: 5.3.3 transitivePeerDependencies: - supports-color @@ -1362,16 +1362,16 @@ packages: '@typescript-eslint/visitor-keys': 6.21.0 dev: true - /@typescript-eslint/scope-manager@7.0.1: - resolution: {integrity: sha512-v7/T7As10g3bcWOOPAcbnMDuvctHzCFYCG/8R4bK4iYzdFqsZTbXGln0cZNVcwQcwewsYU2BJLay8j0/4zOk4w==} + /@typescript-eslint/scope-manager@7.0.2: + resolution: {integrity: sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 7.0.1 - '@typescript-eslint/visitor-keys': 7.0.1 + '@typescript-eslint/types': 7.0.2 + '@typescript-eslint/visitor-keys': 7.0.2 dev: true - /@typescript-eslint/type-utils@7.0.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-YtT9UcstTG5Yqy4xtLiClm1ZpM/pWVGFnkAa90UfdkkZsR1eP2mR/1jbHeYp8Ay1l1JHPyGvoUYR6o3On5Nhmw==} + /@typescript-eslint/type-utils@7.0.2(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^8.56.0 @@ -1380,10 +1380,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 7.0.1(typescript@5.3.3) - '@typescript-eslint/utils': 7.0.1(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 7.0.2(typescript@5.3.3) + '@typescript-eslint/utils': 7.0.2(eslint@8.57.0)(typescript@5.3.3) debug: 4.3.4 - eslint: 8.56.0 + eslint: 8.57.0 ts-api-utils: 1.2.1(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: @@ -1395,8 +1395,8 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/types@7.0.1: - resolution: {integrity: sha512-uJDfmirz4FHib6ENju/7cz9SdMSkeVvJDK3VcMFvf/hAShg8C74FW+06MaQPODHfDJp/z/zHfgawIJRjlu0RLg==} + /@typescript-eslint/types@7.0.2: + resolution: {integrity: sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==} engines: {node: ^16.0.0 || >=18.0.0} dev: true @@ -1422,8 +1422,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@7.0.1(typescript@5.3.3): - resolution: {integrity: sha512-SO9wHb6ph0/FN5OJxH4MiPscGah5wjOd0RRpaLvuBv9g8565Fgu0uMySFEPqwPHiQU90yzJ2FjRYKGrAhS1xig==} + /@typescript-eslint/typescript-estree@7.0.2(typescript@5.3.3): + resolution: {integrity: sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -1431,8 +1431,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 7.0.1 - '@typescript-eslint/visitor-keys': 7.0.1 + '@typescript-eslint/types': 7.0.2 + '@typescript-eslint/visitor-keys': 7.0.2 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -1444,38 +1444,38 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.21.0(eslint@8.56.0)(typescript@5.3.3): + /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.3.3): resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.7 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - eslint: 8.56.0 + eslint: 8.57.0 semver: 7.6.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils@7.0.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-oe4his30JgPbnv+9Vef1h48jm0S6ft4mNwi9wj7bX10joGn07QRfqIqFHoMiajrtoU88cIhXf8ahwgrcbNLgPA==} + /@typescript-eslint/utils@7.0.2(eslint@8.57.0)(typescript@5.3.3): + resolution: {integrity: sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^8.56.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.7 - '@typescript-eslint/scope-manager': 7.0.1 - '@typescript-eslint/types': 7.0.1 - '@typescript-eslint/typescript-estree': 7.0.1(typescript@5.3.3) - eslint: 8.56.0 + '@typescript-eslint/scope-manager': 7.0.2 + '@typescript-eslint/types': 7.0.2 + '@typescript-eslint/typescript-estree': 7.0.2(typescript@5.3.3) + eslint: 8.57.0 semver: 7.6.0 transitivePeerDependencies: - supports-color @@ -1490,11 +1490,11 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@7.0.1: - resolution: {integrity: sha512-hwAgrOyk++RTXrP4KzCg7zB2U0xt7RUU0ZdMSCsqF3eKUwkdXUMyTb0qdCuji7VIbcpG62kKTU9M1J1c9UpFBw==} + /@typescript-eslint/visitor-keys@7.0.2: + resolution: {integrity: sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 7.0.1 + '@typescript-eslint/types': 7.0.2 eslint-visitor-keys: 3.4.3 dev: true @@ -1502,38 +1502,38 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vitest/expect@1.3.0: - resolution: {integrity: sha512-7bWt0vBTZj08B+Ikv70AnLRicohYwFgzNjFqo9SxxqHHxSlUJGSXmCRORhOnRMisiUryKMdvsi1n27Bc6jL9DQ==} + /@vitest/expect@1.3.1: + resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==} dependencies: - '@vitest/spy': 1.3.0 - '@vitest/utils': 1.3.0 + '@vitest/spy': 1.3.1 + '@vitest/utils': 1.3.1 chai: 4.4.1 dev: true - /@vitest/runner@1.3.0: - resolution: {integrity: sha512-1Jb15Vo/Oy7mwZ5bXi7zbgszsdIBNjc4IqP8Jpr/8RdBC4nF1CTzIAn2dxYvpF1nGSseeL39lfLQ2uvs5u1Y9A==} + /@vitest/runner@1.3.1: + resolution: {integrity: sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==} dependencies: - '@vitest/utils': 1.3.0 + '@vitest/utils': 1.3.1 p-limit: 5.0.0 pathe: 1.1.2 dev: true - /@vitest/snapshot@1.3.0: - resolution: {integrity: sha512-swmktcviVVPYx9U4SEQXLV6AEY51Y6bZ14jA2yo6TgMxQ3h+ZYiO0YhAHGJNp0ohCFbPAis1R9kK0cvN6lDPQA==} + /@vitest/snapshot@1.3.1: + resolution: {integrity: sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==} dependencies: magic-string: 0.30.7 pathe: 1.1.2 pretty-format: 29.7.0 dev: true - /@vitest/spy@1.3.0: - resolution: {integrity: sha512-AkCU0ThZunMvblDpPKgjIi025UxR8V7MZ/g/EwmAGpjIujLVV2X6rGYGmxE2D4FJbAy0/ijdROHMWa2M/6JVMw==} + /@vitest/spy@1.3.1: + resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==} dependencies: tinyspy: 2.2.1 dev: true - /@vitest/utils@1.3.0: - resolution: {integrity: sha512-/LibEY/fkaXQufi4GDlQZhikQsPO2entBKtfuyIpr1jV4DpaeasqkeHjhdOhU24vSHshcSuEyVlWdzvv2XmYCw==} + /@vitest/utils@1.3.1: + resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==} dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 @@ -2195,7 +2195,7 @@ packages: common-tags: 1.8.2 debug: 4.3.4 fs-jetpack: 3.2.0 - isomorphic-git: 1.25.5 + isomorphic-git: 1.25.6 parse-git-config: 3.0.0 parse-github-url: 1.0.2 request: 2.88.2 @@ -2295,16 +2295,16 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-prettier@9.1.0(eslint@8.56.0): + /eslint-config-prettier@9.1.0(eslint@8.57.0): resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.56.0 + eslint: 8.57.0 dev: true - /eslint-config-prisma@0.2.0(@typescript-eslint/eslint-plugin@7.0.1)(@typescript-eslint/parser@7.0.1)(eslint-plugin-deprecation@2.0.0)(eslint-plugin-only-warn@1.1.0)(eslint-plugin-prefer-arrow@1.2.3)(eslint-plugin-simple-import-sort@12.0.0)(eslint-plugin-tsdoc@0.2.17)(eslint@8.56.0): + /eslint-config-prisma@0.2.0(@typescript-eslint/eslint-plugin@7.0.2)(@typescript-eslint/parser@7.0.2)(eslint-plugin-deprecation@2.0.0)(eslint-plugin-only-warn@1.1.0)(eslint-plugin-prefer-arrow@1.2.3)(eslint-plugin-simple-import-sort@12.0.0)(eslint-plugin-tsdoc@0.2.17)(eslint@8.57.0): resolution: {integrity: sha512-ky6iBCU9jk4o/SqkUmIUwspXjTfDO/d4glQ1VHrkL/SBra+PZjzsrDVX3pdXZgDw81+NFVYEBJIL4+lBL/yMCw==} peerDependencies: '@typescript-eslint/eslint-plugin': ^6 @@ -2316,25 +2316,25 @@ packages: eslint-plugin-simple-import-sort: ^10.0 eslint-plugin-tsdoc: ^0.2 dependencies: - '@typescript-eslint/eslint-plugin': 7.0.1(@typescript-eslint/parser@7.0.1)(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/parser': 7.0.1(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 - eslint-config-prettier: 9.1.0(eslint@8.56.0) - eslint-plugin-deprecation: 2.0.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/eslint-plugin': 7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.0.2(eslint@8.57.0)(typescript@5.3.3) + eslint: 8.57.0 + eslint-config-prettier: 9.1.0(eslint@8.57.0) + eslint-plugin-deprecation: 2.0.0(eslint@8.57.0)(typescript@5.3.3) eslint-plugin-only-warn: 1.1.0 - eslint-plugin-prefer-arrow: 1.2.3(eslint@8.56.0) - eslint-plugin-simple-import-sort: 12.0.0(eslint@8.56.0) + eslint-plugin-prefer-arrow: 1.2.3(eslint@8.57.0) + eslint-plugin-simple-import-sort: 12.0.0(eslint@8.57.0) eslint-plugin-tsdoc: 0.2.17 dev: true - /eslint-plugin-deprecation@2.0.0(eslint@8.56.0)(typescript@5.3.3): + /eslint-plugin-deprecation@2.0.0(eslint@8.57.0)(typescript@5.3.3): resolution: {integrity: sha512-OAm9Ohzbj11/ZFyICyR5N6LbOIvQMp7ZU2zI7Ej0jIc8kiGUERXPNMfw2QqqHD1ZHtjMub3yPZILovYEYucgoQ==} peerDependencies: eslint: ^7.0.0 || ^8.0.0 typescript: ^4.2.4 || ^5.0.0 dependencies: - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.3.3) - eslint: 8.56.0 + '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3) + eslint: 8.57.0 tslib: 2.6.2 tsutils: 3.21.0(typescript@5.3.3) typescript: 5.3.3 @@ -2347,20 +2347,20 @@ packages: engines: {node: '>=6'} dev: true - /eslint-plugin-prefer-arrow@1.2.3(eslint@8.56.0): + /eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0): resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} peerDependencies: eslint: '>=2.0.0' dependencies: - eslint: 8.56.0 + eslint: 8.57.0 dev: true - /eslint-plugin-simple-import-sort@12.0.0(eslint@8.56.0): + /eslint-plugin-simple-import-sort@12.0.0(eslint@8.57.0): resolution: {integrity: sha512-8o0dVEdAkYap0Cn5kNeklaKcT1nUsa3LITWEuFk3nJifOoD+5JQGoyDUW2W/iPWwBsNBJpyJS9y4je/BgxLcyQ==} peerDependencies: eslint: '>=5.0.0' dependencies: - eslint: 8.56.0 + eslint: 8.57.0 dev: true /eslint-plugin-tsdoc@0.2.17: @@ -2383,15 +2383,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.56.0: - resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.10.0 '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.56.0 + '@eslint/js': 8.57.0 '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -2483,7 +2483,7 @@ packages: human-signals: 5.0.0 is-stream: 3.0.0 merge-stream: 2.0.0 - npm-run-path: 5.2.0 + npm-run-path: 5.3.0 onetime: 6.0.0 signal-exit: 4.1.0 strip-final-newline: 3.0.0 @@ -2613,13 +2613,13 @@ packages: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flatted: 3.2.9 + flatted: 3.3.1 keyv: 4.5.4 rimraf: 3.0.2 dev: true - /flatted@3.2.9: - resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true /forever-agent@0.6.1: @@ -2692,7 +2692,7 @@ packages: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - has-proto: 1.0.1 + has-proto: 1.0.3 has-symbols: 1.0.3 hasown: 2.0.1 dev: true @@ -2796,8 +2796,8 @@ packages: resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - /happy-dom@13.3.8: - resolution: {integrity: sha512-RAbq4oYfJNkVan1m1F3jfA4YEyRY0/ASoNvZsNJbuX85jIypidmsz9jQZD7Tqz0VXA2MhAGfcsh5oshwmwNYSg==} + /happy-dom@13.5.0: + resolution: {integrity: sha512-0XWOSvyc03Z2Ye+VGP5pN6fpwgKMy3a2d09RuGRqAR4TEaW0SCtw3upt7dLoKaYBFUO+JeuDJh366aDBat3OVQ==} engines: {node: '>=16.0.0'} dependencies: entities: 4.5.0 @@ -2830,8 +2830,8 @@ packages: es-define-property: 1.0.0 dev: true - /has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} dev: true @@ -3024,8 +3024,8 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /isomorphic-git@1.25.5: - resolution: {integrity: sha512-gEsQpICiw7u68pitnHdJFe3ep6sLbz45uHdGac1NSKPm+Q22hxenIH9uXvXdCYfx7NhwOV7dUKgVk6rKzeNmCA==} + /isomorphic-git@1.25.6: + resolution: {integrity: sha512-zA3k3QOO7doqOnBgwsaXJwHKSIIl5saEdH4xxalu082WHVES4KghsG6RE2SDwjXMCIlNa1bWocbitH6bRIrmLQ==} engines: {node: '>=12'} hasBin: true dependencies: @@ -3129,7 +3129,7 @@ packages: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} dependencies: - mlly: 1.5.0 + mlly: 1.6.1 pkg-types: 1.0.3 dev: true @@ -3441,8 +3441,8 @@ packages: minimist: 1.2.8 dev: true - /mlly@1.5.0: - resolution: {integrity: sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==} + /mlly@1.6.1: + resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} dependencies: acorn: 8.11.3 pathe: 1.1.2 @@ -3493,8 +3493,8 @@ packages: whatwg-url: 5.0.0 dev: true - /npm-run-path@5.2.0: - resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} + /npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: path-key: 4.0.0 @@ -3670,7 +3670,7 @@ packages: resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} dependencies: jsonc-parser: 3.2.1 - mlly: 1.5.0 + mlly: 1.6.1 pathe: 1.1.2 dev: true @@ -4228,8 +4228,8 @@ packages: engines: {node: '>=10'} dev: true - /type-fest@4.10.2: - resolution: {integrity: sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==} + /type-fest@4.10.3: + resolution: {integrity: sha512-JLXyjizi072smKGGcZiAJDCNweT8J+AuRxmPZ1aG7TERg4ijx9REl8CNhbr36RV4qXqL1gO1FF9HL8OkVmmrsA==} engines: {node: '>=16'} dev: true @@ -4372,8 +4372,8 @@ packages: vfile-message: 2.0.4 dev: true - /vite-node@1.3.0(@types/node@20.11.19): - resolution: {integrity: sha512-D/oiDVBw75XMnjAXne/4feCkCEwcbr2SU1bjAhCcfI5Bq3VoOHji8/wCPAfUkDIeohJ5nSZ39fNxM3dNZ6OBOA==} + /vite-node@1.3.1(@types/node@20.11.20): + resolution: {integrity: sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true dependencies: @@ -4381,7 +4381,7 @@ packages: debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.1.3(@types/node@20.11.19) + vite: 5.1.4(@types/node@20.11.20) transitivePeerDependencies: - '@types/node' - less @@ -4393,8 +4393,8 @@ packages: - terser dev: true - /vite@5.1.3(@types/node@20.11.19): - resolution: {integrity: sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==} + /vite@5.1.4(@types/node@20.11.20): + resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -4421,7 +4421,7 @@ packages: terser: optional: true dependencies: - '@types/node': 20.11.19 + '@types/node': 20.11.20 esbuild: 0.19.12 postcss: 8.4.35 rollup: 4.12.0 @@ -4429,15 +4429,15 @@ packages: fsevents: 2.3.3 dev: true - /vitest@1.3.0(@types/node@20.11.19)(happy-dom@13.3.8): - resolution: {integrity: sha512-V9qb276J1jjSx9xb75T2VoYXdO1UKi+qfflY7V7w93jzX7oA/+RtYE6TcifxksxsZvygSSMwu2Uw6di7yqDMwg==} + /vitest@1.3.1(@types/node@20.11.20)(happy-dom@13.5.0): + resolution: {integrity: sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.3.0 - '@vitest/ui': 1.3.0 + '@vitest/browser': 1.3.1 + '@vitest/ui': 1.3.1 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -4454,17 +4454,17 @@ packages: jsdom: optional: true dependencies: - '@types/node': 20.11.19 - '@vitest/expect': 1.3.0 - '@vitest/runner': 1.3.0 - '@vitest/snapshot': 1.3.0 - '@vitest/spy': 1.3.0 - '@vitest/utils': 1.3.0 + '@types/node': 20.11.20 + '@vitest/expect': 1.3.1 + '@vitest/runner': 1.3.1 + '@vitest/snapshot': 1.3.1 + '@vitest/spy': 1.3.1 + '@vitest/utils': 1.3.1 acorn-walk: 8.3.2 chai: 4.4.1 debug: 4.3.4 execa: 8.0.1 - happy-dom: 13.3.8 + happy-dom: 13.5.0 local-pkg: 0.5.0 magic-string: 0.30.7 pathe: 1.1.2 @@ -4473,8 +4473,8 @@ packages: strip-literal: 2.0.0 tinybench: 2.6.0 tinypool: 0.8.2 - vite: 5.1.3(@types/node@20.11.19) - vite-node: 1.3.0(@types/node@20.11.19) + vite: 5.1.4(@types/node@20.11.20) + vite-node: 1.3.1(@types/node@20.11.20) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 940bc892e..9f2e9fcd7 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -1,26 +1,28 @@ import { expectTypeOf, test } from 'vitest' import type * as Schema from '../../tests/builder/_/schema.js' +import type { SelectionSet } from '../SelectionSet/__.js' import type { ResultSet } from './__.js' type I = Schema.$.Index +type RS<$S extends SelectionSet.Query> = ResultSet.Query<$S, I> + // dprint-ignore test(`general`, () => { - // scalar - expectTypeOf>().toEqualTypeOf<{ - string: string | null - }>() + // Scalar + expectTypeOf>().toEqualTypeOf<{ id: null | string }>() + + // Object + expectTypeOf>().toEqualTypeOf<{ object: null | { id: string | null } }>() // Arguments // scalar - expectTypeOf>().toEqualTypeOf<{ - stringWithArgs: null | string - }>() + expectTypeOf>().toEqualTypeOf<{ stringWithArgs: null | string }>() + expectTypeOf>().toEqualTypeOf<{ stringWithArgs: null | string }>() - // error: unknown field - expectTypeOf>().toEqualTypeOf<{ - string2: ResultSet.Errors.UnknownFieldName<'string2', Schema.Root.Query> - }>() + // Errors + // unknown field + expectTypeOf>().toEqualTypeOf<{ id2: ResultSet.Errors.UnknownFieldName<'id2', Schema.Root.Query> }>() // assertType>({string:''}) }) diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index fa1e74545..fb9d50213 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -2,12 +2,22 @@ import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/__.js' -import type * as SelectionSet from '../SelectionSet/SelectionSet.js' -export type Query< - $SelectionSetQuery extends object, - $Index extends Schema.Index, -> = Object<$SelectionSetQuery, Exclude<$Index['Root']['Query'], null>> +import type { ConditionalSimplifyDeep } from 'type-fest/source/conditional-simplify.js' + +type SimplifyDeep = ConditionalSimplifyDeep | Date, object> // eslint-disable-line + +// dprint-ignore +export type Query<$SelectionSetQuery extends object, $Index extends Schema.Index> = + SimplifyDeep>> + +// dprint-ignore +export type Mutation<$SelectionSetMutation extends object, $Index extends Schema.Index> = + SimplifyDeep>> + +// dprint-ignore +export type Subscription<$SelectionSetSubscription extends object, $Index extends Schema.Index> = + SimplifyDeep>> // // dprint-ignore // export type ResultSet<$SelectionSet extends object, $Node extends Schema.Node> = @@ -15,28 +25,33 @@ export type Query< // ? null | ResultSet<$SelectionSet, Exclude<$Node, Schema.Nullable>> // : ResultSetObject<$SelectionSet, $Node> -export type Object< - $SelectionSet extends object, - $Object extends Schema.Object, -> = { - [$Key in keyof SelectionSet.OmitArgs<$SelectionSet> & - string]: $Key extends keyof $Object - ? Field> - : Errors.UnknownFieldName<$Key, $Object> +export type Object<$SelectionSet, $Object extends Schema.Object> = { + [$SSKey in keyof $SelectionSet & string]: + $SSKey extends keyof $Object + ? SimplifyDeep>> + : Errors.UnknownFieldName<$SSKey, $Object> } -type Field<$Field extends Schema.Field> = - | $Field['type'] - | ($Field['nullable'] extends true ? null : never) +// dprint-ignore +type Field<$SelectionSet, $Field extends Schema.Field> = + | FieldNullable<$Field> + | Node<$SelectionSet, $Field['type']> +type FieldNullable<$Field extends Schema.Field> = $Field['nullable'] extends true ? null : never + +// dprint-ignore +type Node<$SelectionSet, $Node extends Schema.Node> = + $Node extends Schema.Object ? Object<$SelectionSet, $Node> : + $Node extends Schema.Scalar ? $Node + : Errors.UnknownNode<$Node> + +// dprint-ignore export namespace Errors { - export type UnknownFieldName< - $FieldName extends string, - $Node extends Schema.Object, - > = TSError< - 'ResultSetObject', - `field "${$FieldName}" does not exist on schema object "${$Node['__typename']['type']}"` - > + export type UnknownNode<$Node extends Schema.Node> = + TSError<'Node', `Unknown case`, { $Node: $Node }> + + export type UnknownFieldName<$FieldName extends string, $Node extends Schema.Object> = + TSError<'ResultSetObject', `field "${$FieldName}" does not exist on schema object "${$Node['__typename']['type']}"`> } // type SelectField<$Objekt extends Schema.Object, $SelectionObjekt extends SelectionObjekt, $FieldName extends keyof $SelectionObjekt & string> = diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 97a5748bb..4f9ceedb9 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -6,21 +6,27 @@ type S = Query test(`general`, () => { // scalar - assertType({ string: true }) - assertType({ string: false }) - assertType({ string: 1 }) - assertType({ string: 0 }) - assertType({ string: undefined }) + assertType({ id: true }) + assertType({ id: false }) + assertType({ id: 1 }) + assertType({ id: 0 }) + assertType({ id: undefined }) - // object type + // scalar non-null + assertType({ idNonNull: true }) + + // Object // @ts-expect-error excess property check - assertType({ string2: true }) + assertType({ id2: true }) // @ts-expect-error excess property check assertType({ object: { a2: true } }) assertType({ __typename: true }) - assertType({ object: { a: true } }) + assertType({ object: { id: true } }) + + // Object Non-Null + assertType({ objectNonNull: { id: true } }) - // union type + // Union assertType({ fooBarUnion: { __typename: true } }) assertType({ fooBarUnion: { onFoo: { __typename: true } } }) assertType({ fooBarUnion: { onFoo: { a: true } } }) @@ -37,13 +43,13 @@ test(`general`, () => { // alias: enum assertType({ abcEnum_as_enum: true }) // alias: object - assertType({ object_as_o: { a: true } }) + assertType({ object_as_o: { id: true } }) // @ts-expect-error invalid alias key format - assertType({ object_as_: { a: true } }) + assertType({ object_as_: { id: true } }) // @ts-expect-error invalid alias key format - assertType({ object_as: { a: true } }) + assertType({ object_as: { id: true } }) // @ts-expect-error invalid alias key format - assertType({ object2_as_o: { a: true } }) + assertType({ object2_as_o: { id: true } }) // directives // @skip @@ -56,7 +62,7 @@ test(`general`, () => { assertType({ string: { $skip: {} } }) // assertType({ string: skip() }) // on object - assertType({ object: { $skip: true, a: true } }) + assertType({ object: { $skip: true, string: true } }) // assertType({ scalars: skip().select({ a: true }) }) // on fragment assertType({ fooBarUnion: { onBar: { $skip: true, b: true } } }) @@ -84,13 +90,13 @@ test(`general`, () => { assertType({ string: { $stream: {} } }) // group of fields - assertType({ object: { ___: { $skip: true, a: true, b: true } } }) - assertType({ object: { ___: [{ $skip: true, a: true, b: true }] } }) + assertType({ object: { ___: { $skip: true, int: true, id: true } } }) + assertType({ object: { ___: [{ $skip: true, int: true, id: true }] } }) // Arguments // all-optional on object - assertType({ objectWithArgs: { $: {}, a: true } }) - assertType({ objectWithArgs: { a: true } }) + assertType({ objectWithArgs: { $: {}, id: true } }) + assertType({ objectWithArgs: { id: true } }) assertType({ objectWithArgs: { $: { @@ -100,7 +106,7 @@ test(`general`, () => { int: 3, string: `abc`, }, - a: true, + id: true, }, }) // builder interface diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index da62ad5b9..2df4f416d 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -4,51 +4,49 @@ import type { MaybeList, NonEmptyString } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/__.js' -export type OmitArgs = Omit +export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Object + ? SelectionSetObject<$Index['Root']['Query'], $Index> + : never -export type Query<$Index extends Schema.Index> = - $Index['Root']['Query'] extends Object - ? SelectionSetObject<$Index['Root']['Query'], $Index> - : never +export type Mutation<$Index extends Schema.Index> = $Index['Root']['Mutation'] extends Object + ? SelectionSetObject<$Index['Root']['Mutation'], $Index> + : never -export type Mutation<$Index extends Schema.Index> = - $Index['Root']['Mutation'] extends Object - ? SelectionSetObject<$Index['Root']['Mutation'], $Index> - : never - -export type Subscription<$Index extends Schema.Index> = - $Index['Root']['Subscription'] extends Object - ? SelectionSetObject<$Index['Root']['Subscription'], $Index> - : never +export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscription'] extends Object + ? SelectionSetObject<$Index['Root']['Subscription'], $Index> + : never type SelectionSetObject< $Object extends Schema.Object, $Index extends Schema.Index, -> = { - [Key in keyof $Object]?: SelectionSetField< - Schema.AsField<$Object[Key]>, - $Index - > -} & /** - * Alias support. - * Allow every field to also be given as a key with this pattern `_as_: ...` - */ -{ - [Key in keyof $Object as `${keyof OmitArgs<$Object> & - string}_as_${NonEmptyString}`]?: SelectionSetField< - Schema.AsField<$Object[Key]>, - $Index - > -} & FieldDirectives & /** +> = + & { + [Key in keyof $Object]?: SelectionSetField< + Schema.AsField<$Object[Key]>, + $Index + > + } + & /** + * Alias support. + * Allow every field to also be given as a key with this pattern `_as_: ...` + */ { + [ + Key in keyof $Object as `${keyof $Object & string}_as_${NonEmptyString}` + ]?: SelectionSetField< + Schema.AsField<$Object[Key]>, + $Index + > + } + & FieldDirectives + & /** * Inline fragments for field groups. * @see https://spec.graphql.org/draft/#sec-Inline-Fragments - */ - { + */ { ___?: MaybeList> - } & /** + } + & /** * Special property to select all scalars. - */ - { $scalars?: ClientIndicator } + */ { $scalars?: ClientIndicator } // dprint-ignore export type SelectionSetField< @@ -63,25 +61,26 @@ export type SelectionSetField< : TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.Args - ? $Field['args']['allOptional'] extends true - ? { - $?: $Field['args']['type'] - } - : { - $: $Field['args']['type'] - } + ? $Field['args']['allOptional'] extends true ? { + $?: $Field['args']['type'] + } + : { + $: $Field['args']['type'] + } : {} // TODO why does $object not get passed to this in a distributed way? type SelectionSetUnion< $Object extends Schema.Union, $Index extends Schema.Index, -> = { - [Key in $Object['__typename']['type'] as `on${Capitalize}`]?: SelectionSetObject< - Schema.OmitUnionBrand>, - $Index - > -} & { __typename?: NoArgsIndicator } +> = + & { + [Key in $Object['__typename']['type'] as `on${Capitalize}`]?: SelectionSetObject< + Schema.OmitUnionBrand>, + $Index + > + } + & { __typename?: NoArgsIndicator } /** * Helpers diff --git a/src/lib/client.ts b/src/lib/client.ts index f933f0c7d..218d8ebe8 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -1,35 +1,21 @@ import type { ResultSet } from '../ResultSet/__.js' -import type { Query } from '../ResultSet/ResultSet.js' import type { Index } from '../schema/Schema.js' import type { SelectionSet } from '../SelectionSet/__.js' +// dprint-ignore export type Client<$SchemaIndex extends Index> = - ($SchemaIndex['Root']['Query'] extends null - ? { - // dprint-ignore - query: <$SelectionSet extends SelectionSet.Query<$SchemaIndex>>( - selectionSet: $SelectionSet, - ) => Promise> - } - : unknown) & - ($SchemaIndex['Root']['Mutation'] extends null - ? { - // dprint-ignore - mutation: <$SelectionSet extends SelectionSet.Mutation<$SchemaIndex>>( - selectionSet: $SelectionSet, - ) => Promise> - } - : unknown) & - ($SchemaIndex['Root']['Subscription'] extends null - ? { - // dprint-ignore - subscription: < - $SelectionSet extends SelectionSet.Subscription<$SchemaIndex>, - >( - selectionSet: $SelectionSet, - ) => Promise> - } - : unknown) + & ($SchemaIndex['Root']['Query'] extends null ? { + query: <$SelectionSet extends SelectionSet.Query<$SchemaIndex>>(selectionSet: $SelectionSet) => Promise> + } + : unknown) + & ($SchemaIndex['Root']['Mutation'] extends null ? { + mutation: <$SelectionSet extends SelectionSet.Mutation<$SchemaIndex>>(selectionSet: $SelectionSet) => Promise> + } + : unknown) + & ($SchemaIndex['Root']['Subscription'] extends null ? { + subscription: <$SelectionSet extends SelectionSet.Subscription<$SchemaIndex>>(selectionSet: $SelectionSet) => Promise> + } + : unknown) interface Input { url: URL | string diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index 51cf5cd83..0f45f327a 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -1,8 +1,11 @@ type Query { + id: ID + idNonNull: ID! string: String stringWithRequiredArg(string:String!): String stringWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): String object: Object + objectNonNull: Object! objectWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): Object fooBarUnion: FooBarUnion """ @@ -31,11 +34,11 @@ type Bar { } type Object { - a: String - b: Int - c: Float - d: Boolean - e: ID + string: String + int: Int + float: Float + boolean: Boolean + id: ID } """ diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 651b582b2..e65864bb4 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -1,22 +1,24 @@ export namespace $ { - export interface Index { - Root: { - Query: Root.Query - Mutation: null - Subscription: null - } - unions: { - Union: Union.FooBarUnion - } - scalars: Scalars - } - export interface Scalars { - String: string - Int: number - Float: number - Boolean: boolean +export interface Index { +Root: { +Query: Root.Query +Mutation: null +Subscription: null +} +unions: { +Union: Union.FooBarUnion +} +scalars: Scalars +} +export interface Scalars { + ID: string - } +String: string +Int: number +Float: number +Boolean: boolean + +} } // ------------------------------------------------------------ // @@ -24,74 +26,89 @@ export namespace $ { // ------------------------------------------------------------ // export namespace Root { - export interface Query { - __typename: { - type: 'Query' - nullable: false - args: null - } - string: { - type: $.Scalars['String'] - nullable: true - args: null - } - stringWithRequiredArg: { - type: $.Scalars['String'] - nullable: true - args: { - type: { - string: $.Scalars['String'] - } - allOptional: false - } - } - stringWithArgs: { - type: $.Scalars['String'] - nullable: true - args: { - type: { - string?: $.Scalars['String'] | null - int?: $.Scalars['Int'] | null - float?: $.Scalars['Float'] | null - boolean?: $.Scalars['Boolean'] | null - id?: $.Scalars['ID'] | null - } - allOptional: true - } - } - object: { - type: Object.Object - nullable: true - args: null - } - objectWithArgs: { - type: Object.Object - nullable: true - args: { - type: { - string?: $.Scalars['String'] | null - int?: $.Scalars['Int'] | null - float?: $.Scalars['Float'] | null - boolean?: $.Scalars['Boolean'] | null - id?: $.Scalars['ID'] | null - } - allOptional: true - } - } - fooBarUnion: { - type: Union.FooBarUnion - nullable: true - args: null - } - /** - * Query enum field documentation. - */ - abcEnum: { - type: Enum.ABCEnum - nullable: true - args: null - } - } +export interface Query { +__typename: { +type: "Query" +nullable: false +args: null +} +id: { +type: $.Scalars["ID"] +nullable: true +args: null +} +idNonNull: { +type: $.Scalars["ID"] +nullable: false +args: null +} +string: { +type: $.Scalars["String"] +nullable: true +args: null +} +stringWithRequiredArg: { +type: $.Scalars["String"] +nullable: true +args: { +type: { +string: $.Scalars["String"] +} +allOptional: false +} +} +stringWithArgs: { +type: $.Scalars["String"] +nullable: true +args: { +type: { +string?: $.Scalars["String"] | null +int?: $.Scalars["Int"] | null +float?: $.Scalars["Float"] | null +boolean?: $.Scalars["Boolean"] | null +id?: $.Scalars["ID"] | null +} +allOptional: true +} +} +object: { +type: Object.Object +nullable: true +args: null +} +objectNonNull: { +type: Object.Object +nullable: false +args: null +} +objectWithArgs: { +type: Object.Object +nullable: true +args: { +type: { +string?: $.Scalars["String"] | null +int?: $.Scalars["Int"] | null +float?: $.Scalars["Float"] | null +boolean?: $.Scalars["Boolean"] | null +id?: $.Scalars["ID"] | null +} +allOptional: true +} +} +fooBarUnion: { +type: Union.FooBarUnion +nullable: true +args: null +} +/** +* Query enum field documentation. +*/ +abcEnum: { +type: Enum.ABCEnum +nullable: true +args: null +} +} } // ------------------------------------------------------------ // @@ -99,18 +116,18 @@ export namespace Root { // ------------------------------------------------------------ // export namespace Enum { - /** - * Enum documentation. - * - * Members - * "A" - (DEPRECATED: Enum value A is deprecated.) - * "B" - Enum B member documentation. - * "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) - */ - export type ABCEnum = - | 'A' - | 'B' - | 'C' +/** +* Enum documentation. +* +* Members +* "A" - (DEPRECATED: Enum value A is deprecated.) +* "B" - Enum B member documentation. +* "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) +*/ +export type ABCEnum = +| "A" +| "B" +| "C" } // ------------------------------------------------------------ // @@ -118,7 +135,8 @@ export namespace Enum { // ------------------------------------------------------------ // export namespace InputObject { - // -- no types -- +// -- no types -- + } // ------------------------------------------------------------ // @@ -126,7 +144,8 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { - // -- no types -- +// -- no types -- + } // ------------------------------------------------------------ // @@ -134,72 +153,72 @@ export namespace Interface { // ------------------------------------------------------------ // export namespace Object { - /** - * Object documentation. - */ - export interface Foo { - __typename: { - type: 'Foo' - nullable: false - args: null - } - /** - * Field documentation. - * - * @deprecated Field a is deprecated. - */ - a: { - type: $.Scalars['String'] - nullable: true - args: null - } - } - - export interface Bar { - __typename: { - type: 'Bar' - nullable: false - args: null - } - b: { - type: $.Scalars['Int'] - nullable: true - args: null - } - } - - export interface Object { - __typename: { - type: 'Object' - nullable: false - args: null - } - a: { - type: $.Scalars['String'] - nullable: true - args: null - } - b: { - type: $.Scalars['Int'] - nullable: true - args: null - } - c: { - type: $.Scalars['Float'] - nullable: true - args: null - } - d: { - type: $.Scalars['Boolean'] - nullable: true - args: null - } - e: { - type: $.Scalars['ID'] - nullable: true - args: null - } - } +/** +* Object documentation. +*/ +export interface Foo { +__typename: { +type: "Foo" +nullable: false +args: null +} +/** +* Field documentation. +* +* @deprecated Field a is deprecated. +*/ +a: { +type: $.Scalars["String"] +nullable: true +args: null +} +} + +export interface Bar { +__typename: { +type: "Bar" +nullable: false +args: null +} +b: { +type: $.Scalars["Int"] +nullable: true +args: null +} +} + +export interface Object { +__typename: { +type: "Object" +nullable: false +args: null +} +string: { +type: $.Scalars["String"] +nullable: true +args: null +} +int: { +type: $.Scalars["Int"] +nullable: true +args: null +} +float: { +type: $.Scalars["Float"] +nullable: true +args: null +} +boolean: { +type: $.Scalars["Boolean"] +nullable: true +args: null +} +id: { +type: $.Scalars["ID"] +nullable: true +args: null +} +} } // ------------------------------------------------------------ // @@ -207,10 +226,10 @@ export namespace Object { // ------------------------------------------------------------ // export namespace Union { - /** - * Union documentation. - */ - export type FooBarUnion = - | Object.Foo & { $$union: true } - | Object.Bar & { $$union: true } -} +/** +* Union documentation. +*/ +export type FooBarUnion = +| Object.Foo& { $$union:true} +| Object.Bar& { $$union:true} +} \ No newline at end of file diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index 9818bfef8..d03a658e9 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -15,11 +15,11 @@ scalars: Scalars } export interface Scalars { - String: string + ID: string +String: string Int: number Float: number Boolean: boolean -ID: string } } @@ -35,6 +35,16 @@ type: "Query" nullable: false args: null } +id: { +type: $.Scalars["ID"] +nullable: true +args: null +} +idNonNull: { +type: $.Scalars["ID"] +nullable: false +args: null +} string: { type: $.Scalars["String"] nullable: true @@ -69,6 +79,11 @@ type: Object.Object nullable: true args: null } +objectNonNull: { +type: Object.Object +nullable: false +args: null +} objectWithArgs: { type: Object.Object nullable: true @@ -181,27 +196,27 @@ type: "Object" nullable: false args: null } -a: { +string: { type: $.Scalars["String"] nullable: true args: null } -b: { +int: { type: $.Scalars["Int"] nullable: true args: null } -c: { +float: { type: $.Scalars["Float"] nullable: true args: null } -d: { +boolean: { type: $.Scalars["Boolean"] nullable: true args: null } -e: { +id: { type: $.Scalars["ID"] nullable: true args: null From 4778b49c99047b8c45c66622f1e813e75897cf57 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 23 Feb 2024 19:34:41 -0500 Subject: [PATCH 22/71] scalars wildcard --- src/ResultSet/ResultSet.test-d.ts | 10 ++++++ src/ResultSet/ResultSet.ts | 31 ++++++++++++------- src/SelectionSet/SelectionSet.ts | 6 +++- src/lib/client.ts | 4 +-- tests/builder/_/schema.graphql | 6 ++++ tests/builder/_/schema.ts | 23 ++++++++++++++ .../__snapshots__/generate.test.ts.snap | 23 ++++++++++++++ 7 files changed, 87 insertions(+), 16 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 9f2e9fcd7..c75def205 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -7,13 +7,23 @@ type I = Schema.$.Index type RS<$S extends SelectionSet.Query> = ResultSet.Query<$S, I> +type x = RS<{ objectNonNull: { $scalars: true } }> + // dprint-ignore test(`general`, () => { // Scalar expectTypeOf>().toEqualTypeOf<{ id: null | string }>() + // non-nullable + expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() // Object expectTypeOf>().toEqualTypeOf<{ object: null | { id: string | null } }>() + // non-nullable + expectTypeOf>().toEqualTypeOf<{ objectNonNull: { id: string | null } }>() + // scalars-wildcard + expectTypeOf>().toEqualTypeOf<{ objectNonNull: { __typename: "Object"; string: null|string; int: null|number; float: null|number; boolean: null|boolean; id: null|string; } }>() + // scalars-wildcard with nested object + expectTypeOf>().toEqualTypeOf<{ objectNested: null | { __typename: "ObjectNested"; id: null|string } }>() // Arguments // scalar diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index fb9d50213..a6168a347 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -2,6 +2,7 @@ import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/__.js' +import type { SelectionSet } from '../SelectionSet/__.js' import type { ConditionalSimplifyDeep } from 'type-fest/source/conditional-simplify.js' @@ -25,12 +26,24 @@ export type Subscription<$SelectionSetSubscription extends object, $Index extend // ? null | ResultSet<$SelectionSet, Exclude<$Node, Schema.Nullable>> // : ResultSetObject<$SelectionSet, $Node> -export type Object<$SelectionSet, $Object extends Schema.Object> = { - [$SSKey in keyof $SelectionSet & string]: - $SSKey extends keyof $Object - ? SimplifyDeep>> - : Errors.UnknownFieldName<$SSKey, $Object> -} +// dprint-ignore +type Node<$SelectionSet, $Node extends Schema.Node> = + $Node extends Schema.Object ? Object<$SelectionSet, $Node> : + $Node extends Schema.Scalar ? $Node + : Errors.UnknownNode<$Node> + +// dprint-ignore +export type Object<$SelectionSet, $Object extends Schema.Object> = + SelectionSet.IsSelectScalarsWildcard<$SelectionSet> extends true + ? { + [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: Field<$SelectionSet, Schema.AsField<$Object[$Key]>> + } + : { + [$SSKey in keyof $SelectionSet & string]: + $SSKey extends keyof $Object + ? SimplifyDeep>> + : Errors.UnknownFieldName<$SSKey, $Object> + } // dprint-ignore type Field<$SelectionSet, $Field extends Schema.Field> = @@ -39,12 +52,6 @@ type Field<$SelectionSet, $Field extends Schema.Field> = type FieldNullable<$Field extends Schema.Field> = $Field['nullable'] extends true ? null : never -// dprint-ignore -type Node<$SelectionSet, $Node extends Schema.Node> = - $Node extends Schema.Object ? Object<$SelectionSet, $Node> : - $Node extends Schema.Scalar ? $Node - : Errors.UnknownNode<$Node> - // dprint-ignore export namespace Errors { export type UnknownNode<$Node extends Schema.Node> = diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 2df4f416d..9c9978aee 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -48,6 +48,8 @@ type SelectionSetObject< * Special property to select all scalars. */ { $scalars?: ClientIndicator } +export type IsSelectScalarsWildcard = SS extends { $scalars: ClientIndicatorPositive } ? true : false + // dprint-ignore export type SelectionSetField< $Field extends Schema.Field, @@ -90,7 +92,9 @@ type SelectionSetUnion< /** * Should this field be selected? */ -export type ClientIndicator = boolean | 1 | 0 +export type ClientIndicator = ClientIndicatorPositive | ClientIndicatorNegative +export type ClientIndicatorPositive = true | 1 +export type ClientIndicatorNegative = false | 0 /** * Field selection in general, with directives support too. diff --git a/src/lib/client.ts b/src/lib/client.ts index 218d8ebe8..1dc914641 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -21,8 +21,6 @@ interface Input { url: URL | string } -export const create = <$SchemaIndex extends Index>( - input: Input, -): Client<$SchemaIndex> => { +export const create = <$SchemaIndex extends Index>(input: Input): Client<$SchemaIndex> => { return 1 as any } diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index 0f45f327a..cc216a612 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -5,6 +5,7 @@ type Query { stringWithRequiredArg(string:String!): String stringWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): String object: Object + objectNested: ObjectNested objectNonNull: Object! objectWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): Object fooBarUnion: FooBarUnion @@ -33,6 +34,11 @@ type Bar { b: Int } +type ObjectNested { + id: ID + object: Object +} + type Object { string: String int: Int diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index e65864bb4..d37871617 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -76,6 +76,11 @@ type: Object.Object nullable: true args: null } +objectNested: { +type: Object.ObjectNested +nullable: true +args: null +} objectNonNull: { type: Object.Object nullable: false @@ -187,6 +192,24 @@ args: null } } +export interface ObjectNested { +__typename: { +type: "ObjectNested" +nullable: false +args: null +} +id: { +type: $.Scalars["ID"] +nullable: true +args: null +} +object: { +type: Object.Object +nullable: true +args: null +} +} + export interface Object { __typename: { type: "Object" diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index d03a658e9..16a05bc8d 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -79,6 +79,11 @@ type: Object.Object nullable: true args: null } +objectNested: { +type: Object.ObjectNested +nullable: true +args: null +} objectNonNull: { type: Object.Object nullable: false @@ -190,6 +195,24 @@ args: null } } +export interface ObjectNested { +__typename: { +type: "ObjectNested" +nullable: false +args: null +} +id: { +type: $.Scalars["ID"] +nullable: true +args: null +} +object: { +type: Object.Object +nullable: true +args: null +} +} + export interface Object { __typename: { type: "Object" From e33bf0a6f70526a75ce3fa1d75d967513b5360f3 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 23 Feb 2024 19:39:17 -0500 Subject: [PATCH 23/71] handle negative indicator --- src/ResultSet/ResultSet.test-d.ts | 4 ++++ src/ResultSet/ResultSet.ts | 2 +- src/SelectionSet/SelectionSet.test-d.ts | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index c75def205..dde3e5953 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -13,8 +13,12 @@ type x = RS<{ objectNonNull: { $scalars: true } }> test(`general`, () => { // Scalar expectTypeOf>().toEqualTypeOf<{ id: null | string }>() + expectTypeOf>().toEqualTypeOf<{ id: null | string }>() // non-nullable expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() + // indicator negative + expectTypeOf>().toEqualTypeOf<{ id: null | string }>() + expectTypeOf>().toEqualTypeOf<{ id: null | string }>() // Object expectTypeOf>().toEqualTypeOf<{ object: null | { id: string | null } }>() diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index a6168a347..fa75b3679 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -39,7 +39,7 @@ export type Object<$SelectionSet, $Object extends Schema.Object> = [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: Field<$SelectionSet, Schema.AsField<$Object[$Key]>> } : { - [$SSKey in keyof $SelectionSet & string]: + [$SSKey in keyof $SelectionSet & string as $SelectionSet[$SSKey] extends SelectionSet.ClientIndicatorNegative ? never : $SSKey]: $SSKey extends keyof $Object ? SimplifyDeep>> : Errors.UnknownFieldName<$SSKey, $Object> diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 4f9ceedb9..32234cb61 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -150,4 +150,5 @@ test(`general`, () => { // todo empty selection set not allowed // // @ts-expect-error empty selection set not allowed // assertType({ scalars: {} }) + // todo selection set of _only_ negative indicators should not be allowed }) From 46ba2a3caaa4380b42f985f765b1e806b3538c95 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 23 Feb 2024 19:41:04 -0500 Subject: [PATCH 24/71] handle undefined negative indicator case --- src/ResultSet/ResultSet.test-d.ts | 1 + src/ResultSet/ResultSet.ts | 3 ++- src/SelectionSet/SelectionSet.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index dde3e5953..72e346819 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -19,6 +19,7 @@ test(`general`, () => { // indicator negative expectTypeOf>().toEqualTypeOf<{ id: null | string }>() expectTypeOf>().toEqualTypeOf<{ id: null | string }>() + expectTypeOf>().toEqualTypeOf<{ id: null | string }>() // Object expectTypeOf>().toEqualTypeOf<{ object: null | { id: string | null } }>() diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index fa75b3679..168636da1 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -36,7 +36,8 @@ type Node<$SelectionSet, $Node extends Schema.Node> = export type Object<$SelectionSet, $Object extends Schema.Object> = SelectionSet.IsSelectScalarsWildcard<$SelectionSet> extends true ? { - [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: Field<$SelectionSet, Schema.AsField<$Object[$Key]>> + [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: + Field<$SelectionSet, Schema.AsField<$Object[$Key]>> } : { [$SSKey in keyof $SelectionSet & string as $SelectionSet[$SSKey] extends SelectionSet.ClientIndicatorNegative ? never : $SSKey]: diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 9c9978aee..bdf416338 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -94,7 +94,7 @@ type SelectionSetUnion< */ export type ClientIndicator = ClientIndicatorPositive | ClientIndicatorNegative export type ClientIndicatorPositive = true | 1 -export type ClientIndicatorNegative = false | 0 +export type ClientIndicatorNegative = false | 0 | undefined /** * Field selection in general, with directives support too. From 33acbe7883b5efad7a9d58a86e5413034444ac08 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 23 Feb 2024 19:44:51 -0500 Subject: [PATCH 25/71] typename case --- src/ResultSet/ResultSet.test-d.ts | 4 +++- src/SelectionSet/SelectionSet.test-d.ts | 15 +++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 72e346819..750564a96 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -7,7 +7,7 @@ type I = Schema.$.Index type RS<$S extends SelectionSet.Query> = ResultSet.Query<$S, I> -type x = RS<{ objectNonNull: { $scalars: true } }> +type x = RS<{ objectNonNull: { __typename: true } }> // dprint-ignore test(`general`, () => { @@ -29,6 +29,8 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ objectNonNull: { __typename: "Object"; string: null|string; int: null|number; float: null|number; boolean: null|boolean; id: null|string; } }>() // scalars-wildcard with nested object expectTypeOf>().toEqualTypeOf<{ objectNested: null | { __typename: "ObjectNested"; id: null|string } }>() + // __typename + expectTypeOf>().toEqualTypeOf<{ objectNonNull: { __typename: "Object" } }>() // Arguments // scalar diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 32234cb61..0ac6f84d5 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -5,16 +5,18 @@ import type { Query } from './SelectionSet.js' type S = Query test(`general`, () => { - // scalar + // Scalar assertType({ id: true }) assertType({ id: false }) assertType({ id: 1 }) assertType({ id: 0 }) assertType({ id: undefined }) - - // scalar non-null + // non-null assertType({ idNonNull: true }) + // Enum + assertType({ abcEnum: true }) + // Object // @ts-expect-error excess property check assertType({ id2: true }) @@ -22,8 +24,7 @@ test(`general`, () => { assertType({ object: { a2: true } }) assertType({ __typename: true }) assertType({ object: { id: true } }) - - // Object Non-Null + // Non-Null assertType({ objectNonNull: { id: true } }) // Union @@ -37,9 +38,7 @@ test(`general`, () => { // @ts-expect-error no a assertType({ fooBarUnion: { onBar: { a: true } } }) - // enum type - assertType({ abcEnum: true }) - + // Alias // alias: enum assertType({ abcEnum_as_enum: true }) // alias: object From 39a843f99b5621ed0b2ccb0fb7a7b86fa26c6c38 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 01:26:18 -0500 Subject: [PATCH 26/71] initial union support --- src/ResultSet/ResultSet.test-d.ts | 9 +++- src/ResultSet/ResultSet.ts | 29 +++++++---- src/SelectionSet/SelectionSet.test-d.ts | 10 ++-- src/SelectionSet/SelectionSet.ts | 18 +++++-- src/generator/generator.ts | 48 ++++++++++++++++--- src/lib/prelude.ts | 4 ++ src/schema/Schema.ts | 8 ++-- tests/builder/_/schema.graphql | 4 +- tests/builder/_/schema.ts | 44 ++++++++++++++--- .../__snapshots__/generate.test.ts.snap | 44 ++++++++++++++--- 10 files changed, 172 insertions(+), 46 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 750564a96..c8036602e 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/ban-types */ import { expectTypeOf, test } from 'vitest' import type * as Schema from '../../tests/builder/_/schema.js' import type { SelectionSet } from '../SelectionSet/__.js' @@ -7,8 +8,6 @@ type I = Schema.$.Index type RS<$S extends SelectionSet.Query> = ResultSet.Query<$S, I> -type x = RS<{ objectNonNull: { __typename: true } }> - // dprint-ignore test(`general`, () => { // Scalar @@ -31,6 +30,12 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ objectNested: null | { __typename: "ObjectNested"; id: null|string } }>() // __typename expectTypeOf>().toEqualTypeOf<{ objectNonNull: { __typename: "Object" } }>() + + // Union + expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | { __typename: "Foo" } | { __typename: "Bar" } }>() + expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | {} | { __typename: "Foo" } }>() + expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | {} | { id: null|string } }>() + expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | { __typename: "Bar" } | { __typename: "Foo"; id: null|string } }>() // Arguments // scalar diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index 168636da1..62bb56cc9 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/ban-types */ +import type { GetKeyOr, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/__.js' import type { SelectionSet } from '../SelectionSet/__.js' @@ -10,15 +11,15 @@ type SimplifyDeep = ConditionalSimplifyDeep | // dprint-ignore export type Query<$SelectionSetQuery extends object, $Index extends Schema.Index> = - SimplifyDeep>> + SimplifyDeep, $Index>> // dprint-ignore export type Mutation<$SelectionSetMutation extends object, $Index extends Schema.Index> = - SimplifyDeep>> + SimplifyDeep, $Index>> // dprint-ignore export type Subscription<$SelectionSetSubscription extends object, $Index extends Schema.Index> = - SimplifyDeep>> + SimplifyDeep, $Index>> // // dprint-ignore // export type ResultSet<$SelectionSet extends object, $Node extends Schema.Node> = @@ -27,29 +28,37 @@ export type Subscription<$SelectionSetSubscription extends object, $Index extend // : ResultSetObject<$SelectionSet, $Node> // dprint-ignore -type Node<$SelectionSet, $Node extends Schema.Node> = - $Node extends Schema.Object ? Object<$SelectionSet, $Node> : +type Node<$SelectionSet, $Node extends Schema.Node, $Index extends Schema.Index> = + $Node extends Schema.Union ? SimplifyDeep> : + $Node extends Schema.Object ? Object<$SelectionSet, $Node, $Index> : $Node extends Schema.Scalar ? $Node : Errors.UnknownNode<$Node> // dprint-ignore -export type Object<$SelectionSet, $Object extends Schema.Object> = +export type Object<$SelectionSet, $Object extends Schema.Object, $Index extends Schema.Index> = SelectionSet.IsSelectScalarsWildcard<$SelectionSet> extends true ? { [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: - Field<$SelectionSet, Schema.AsField<$Object[$Key]>> + Field<$SelectionSet, Schema.AsField<$Object[$Key]>, $Index> } : { [$SSKey in keyof $SelectionSet & string as $SelectionSet[$SSKey] extends SelectionSet.ClientIndicatorNegative ? never : $SSKey]: $SSKey extends keyof $Object - ? SimplifyDeep>> + ? SimplifyDeep, $Index>> : Errors.UnknownFieldName<$SSKey, $Object> } // dprint-ignore -type Field<$SelectionSet, $Field extends Schema.Field> = +type Union<$SelectionSet, $Node extends Schema.Union, $Index extends Schema.Index> = + Values<{ + [$ObjectName in $Node['type']['__typename']['type']]: + Object & SelectionSet.UnionOmitFragments<$SelectionSet>, $Index['objects'][$ObjectName], $Index> + }> + +// dprint-ignore +type Field<$SelectionSet, $Field extends Schema.Field, $Index extends Schema.Index> = | FieldNullable<$Field> - | Node<$SelectionSet, $Field['type']> + | Node<$SelectionSet, $Field['type'], $Index> type FieldNullable<$Field extends Schema.Field> = $Field['nullable'] extends true ? null : never diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 0ac6f84d5..e2b4499b5 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -30,13 +30,13 @@ test(`general`, () => { // Union assertType({ fooBarUnion: { __typename: true } }) assertType({ fooBarUnion: { onFoo: { __typename: true } } }) - assertType({ fooBarUnion: { onFoo: { a: true } } }) + assertType({ fooBarUnion: { onFoo: { id: true } } }) // @ts-expect-error no b - assertType({ fooBarUnion: { onFoo: { b: true } } }) + assertType({ fooBarUnion: { onFoo: { id2: true } } }) assertType({ fooBarUnion: { onBar: { __typename: true } } }) - assertType({ fooBarUnion: { onBar: { b: true } } }) + assertType({ fooBarUnion: { onBar: { int: true } } }) // @ts-expect-error no a - assertType({ fooBarUnion: { onBar: { a: true } } }) + assertType({ fooBarUnion: { onBar: { int2: true } } }) // Alias // alias: enum @@ -64,7 +64,7 @@ test(`general`, () => { assertType({ object: { $skip: true, string: true } }) // assertType({ scalars: skip().select({ a: true }) }) // on fragment - assertType({ fooBarUnion: { onBar: { $skip: true, b: true } } }) + assertType({ fooBarUnion: { onBar: { $skip: true, int: true } } }) // @include assertType({ string: { $include: true } }) assertType({ string: { $include: false } }) diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index bdf416338..57914fe4c 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/ban-types */ -import type { MaybeList, NonEmptyString } from '../lib/prelude.js' +import type { MaybeList, NonEmptyString, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/__.js' @@ -73,12 +73,12 @@ type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.Args // TODO why does $object not get passed to this in a distributed way? type SelectionSetUnion< - $Object extends Schema.Union, + $Union extends Schema.Union, $Index extends Schema.Index, > = & { - [Key in $Object['__typename']['type'] as `on${Capitalize}`]?: SelectionSetObject< - Schema.OmitUnionBrand>, + [Key in $Union['type']['__typename']['type'] as `on${Capitalize}`]?: SelectionSetObject< + Extract<$Union['type'], { __typename: { type: Key } }>, $Index > } @@ -96,6 +96,16 @@ export type ClientIndicator = ClientIndicatorPositive | ClientIndicatorNegative export type ClientIndicatorPositive = true | 1 export type ClientIndicatorNegative = false | 0 | undefined +export type UnionFragmentExtractName = T extends `on${infer $Name}` ? $Name : never +export type UnionExtractFragmentNames = Values< + { + [Key in keyof T]: UnionFragmentExtractName + } +> +export type UnionOmitFragments = { + [$K in keyof T as $K extends `on${NonEmptyString}` ? never : $K]: T[$K] +} + /** * Field selection in general, with directives support too. * If a field directive is given as an indicator then it implies "select this" e.g. `true`/`1`. diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 9ec3b1bf7..845ac4795 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -6,7 +6,16 @@ import type { GraphQLInterfaceType, GraphQLNamedType, } from 'graphql' -import { GraphQLNonNull, GraphQLObjectType, isEnumType } from 'graphql' +import { + GraphQLNonNull, + GraphQLObjectType, + isEnumType, + isInterfaceType, + isNamedType, + isObjectType, + isScalarType, + isUnionType, +} from 'graphql' import { buildSchema } from 'graphql' import _ from 'json-bigint' import fs from 'node:fs/promises' @@ -139,13 +148,21 @@ const concreteRenderers = defineConcreteRenderers({ Code.TSDoc( getDocumentation(config, node), Code.export$( - Code.union( + Code.interface$( node.name, - node - .getTypes() - .map( - (_) => dispatchToPointerRenderer(config, _) + `& { $$union:true}`, + Code.fields([ + Code.field(`__unionname`, Code.quote(node.name)), + Code.field( + `type`, + Code.unionItems( + node + .getTypes() + .map( + (_) => dispatchToPointerRenderer(config, _), + ), + ), ), + ]), ), ), ), @@ -234,9 +251,12 @@ const renderField = (config: Config, field: AnyField): string => { ? renderArgs(config, field.args) : null // const fieldWithArgs = args ? Code.intersection(fieldBase, args) : fieldBase + const name = isNamedType(node) ? node.name : null return Code.object(Code.fields([ Code.field(`type`, type), + // todo keep type name on type node instead + Code.field(`typeName`, name ? Code.quote(name) : `null`), Code.field(`nullable`, nullable ? `true` : `false`), Code.field(`args`, args ?? `null`), ])) @@ -348,6 +368,22 @@ export const generateCode = (input: Input) => { ]), ), ), + Code.field( + `objects`, + Code.object( + Code.fields( + typeMapByKind.GraphQLObjectType.map(_ => Code.field(_.name, Code.propertyAccess(`Object`, _.name))), + ), + ), + ), + Code.field( + `unionMemberNames`, + Code.object( + Code.fields(typeMapByKind.GraphQLUnionType.map( + (_) => Code.field(_.name, Code.unionItems(_.getTypes().map(_ => Code.quote(_.name)))), + )), + ), + ), Code.field( `unions`, Code.object( diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index 13738badf..4155b4e3a 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -153,3 +153,7 @@ export type NonEmptyString = `${Character}${string}` export type MaybeList = T | T[] export type NotEmptyObject = keyof T extends never ? never : T + +export type Values = T[keyof T] + +export type GetKeyOr = Key extends keyof T ? T[Key] : Or diff --git a/src/schema/Schema.ts b/src/schema/Schema.ts index d2fa23d75..ef8708fbe 100644 --- a/src/schema/Schema.ts +++ b/src/schema/Schema.ts @@ -11,13 +11,15 @@ export interface Index { Subscription: null | Object } scalars: object + objects: Record + unionMemberNames: Record } export type Nullable = null // todo needs to be extensible for custom scalars... export type Scalar = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] export type Object = { __typename: { args: null; type: string } } -export type Union = Object & { $$union: true } +export type Union = { __unionname: string; type: Object } export type Node = Object | Union | Scalar | Nullable export type FieldBase<$Type extends Node> = { args: null | Args; type: $Type; nullable: boolean } @@ -29,8 +31,4 @@ export type Field = FieldBase export type Args = { type: object; allOptional: boolean } -export type ExcludeNull = Exclude -export type OmitUnionBrand = Omit -export type OmitArgs = Omit - export type AsField = T extends Field ? T : never diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index cc216a612..668b5a47d 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -27,11 +27,11 @@ type Foo { """ Field documentation. """ - a: String @deprecated(reason: "Field a is deprecated.") + id: ID @deprecated(reason: "Field a is deprecated.") } type Bar { - b: Int + int: Int } type ObjectNested { diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index d37871617..cf38a2458 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -5,6 +5,16 @@ Query: Root.Query Mutation: null Subscription: null } +objects: { +Foo: Object.Foo +Bar: Object.Bar +ObjectNested: Object.ObjectNested +Object: Object.Object +} +unionMemberNames: { +FooBarUnion: "Foo" +| "Bar" +} unions: { Union: Union.FooBarUnion } @@ -34,21 +44,25 @@ args: null } id: { type: $.Scalars["ID"] +typeName: "ID" nullable: true args: null } idNonNull: { type: $.Scalars["ID"] +typeName: "ID" nullable: false args: null } string: { type: $.Scalars["String"] +typeName: "String" nullable: true args: null } stringWithRequiredArg: { type: $.Scalars["String"] +typeName: "String" nullable: true args: { type: { @@ -59,6 +73,7 @@ allOptional: false } stringWithArgs: { type: $.Scalars["String"] +typeName: "String" nullable: true args: { type: { @@ -73,21 +88,25 @@ allOptional: true } object: { type: Object.Object +typeName: "Object" nullable: true args: null } objectNested: { type: Object.ObjectNested +typeName: "ObjectNested" nullable: true args: null } objectNonNull: { type: Object.Object +typeName: "Object" nullable: false args: null } objectWithArgs: { type: Object.Object +typeName: "Object" nullable: true args: { type: { @@ -102,6 +121,7 @@ allOptional: true } fooBarUnion: { type: Union.FooBarUnion +typeName: "FooBarUnion" nullable: true args: null } @@ -110,6 +130,7 @@ args: null */ abcEnum: { type: Enum.ABCEnum +typeName: "ABCEnum" nullable: true args: null } @@ -172,8 +193,9 @@ args: null * * @deprecated Field a is deprecated. */ -a: { -type: $.Scalars["String"] +id: { +type: $.Scalars["ID"] +typeName: "ID" nullable: true args: null } @@ -185,8 +207,9 @@ type: "Bar" nullable: false args: null } -b: { +int: { type: $.Scalars["Int"] +typeName: "Int" nullable: true args: null } @@ -200,11 +223,13 @@ args: null } id: { type: $.Scalars["ID"] +typeName: "ID" nullable: true args: null } object: { type: Object.Object +typeName: "Object" nullable: true args: null } @@ -218,26 +243,31 @@ args: null } string: { type: $.Scalars["String"] +typeName: "String" nullable: true args: null } int: { type: $.Scalars["Int"] +typeName: "Int" nullable: true args: null } float: { type: $.Scalars["Float"] +typeName: "Float" nullable: true args: null } boolean: { type: $.Scalars["Boolean"] +typeName: "Boolean" nullable: true args: null } id: { type: $.Scalars["ID"] +typeName: "ID" nullable: true args: null } @@ -252,7 +282,9 @@ export namespace Union { /** * Union documentation. */ -export type FooBarUnion = -| Object.Foo& { $$union:true} -| Object.Bar& { $$union:true} +export interface FooBarUnion { +__unionname: "FooBarUnion" +type: Object.Foo +| Object.Bar +} } \ No newline at end of file diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index 16a05bc8d..0e95d6bc6 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -8,6 +8,16 @@ Query: Root.Query Mutation: null Subscription: null } +objects: { +Foo: Object.Foo +Bar: Object.Bar +ObjectNested: Object.ObjectNested +Object: Object.Object +} +unionMemberNames: { +FooBarUnion: "Foo" +| "Bar" +} unions: { Union: Union.FooBarUnion } @@ -37,21 +47,25 @@ args: null } id: { type: $.Scalars["ID"] +typeName: "ID" nullable: true args: null } idNonNull: { type: $.Scalars["ID"] +typeName: "ID" nullable: false args: null } string: { type: $.Scalars["String"] +typeName: "String" nullable: true args: null } stringWithRequiredArg: { type: $.Scalars["String"] +typeName: "String" nullable: true args: { type: { @@ -62,6 +76,7 @@ allOptional: false } stringWithArgs: { type: $.Scalars["String"] +typeName: "String" nullable: true args: { type: { @@ -76,21 +91,25 @@ allOptional: true } object: { type: Object.Object +typeName: "Object" nullable: true args: null } objectNested: { type: Object.ObjectNested +typeName: "ObjectNested" nullable: true args: null } objectNonNull: { type: Object.Object +typeName: "Object" nullable: false args: null } objectWithArgs: { type: Object.Object +typeName: "Object" nullable: true args: { type: { @@ -105,6 +124,7 @@ allOptional: true } fooBarUnion: { type: Union.FooBarUnion +typeName: "FooBarUnion" nullable: true args: null } @@ -113,6 +133,7 @@ args: null */ abcEnum: { type: Enum.ABCEnum +typeName: "ABCEnum" nullable: true args: null } @@ -175,8 +196,9 @@ args: null * * @deprecated Field a is deprecated. */ -a: { -type: $.Scalars["String"] +id: { +type: $.Scalars["ID"] +typeName: "ID" nullable: true args: null } @@ -188,8 +210,9 @@ type: "Bar" nullable: false args: null } -b: { +int: { type: $.Scalars["Int"] +typeName: "Int" nullable: true args: null } @@ -203,11 +226,13 @@ args: null } id: { type: $.Scalars["ID"] +typeName: "ID" nullable: true args: null } object: { type: Object.Object +typeName: "Object" nullable: true args: null } @@ -221,26 +246,31 @@ args: null } string: { type: $.Scalars["String"] +typeName: "String" nullable: true args: null } int: { type: $.Scalars["Int"] +typeName: "Int" nullable: true args: null } float: { type: $.Scalars["Float"] +typeName: "Float" nullable: true args: null } boolean: { type: $.Scalars["Boolean"] +typeName: "Boolean" nullable: true args: null } id: { type: $.Scalars["ID"] +typeName: "ID" nullable: true args: null } @@ -255,8 +285,10 @@ export namespace Union { /** * Union documentation. */ -export type FooBarUnion = -| Object.Foo& { $$union:true} -| Object.Bar& { $$union:true} +export interface FooBarUnion { +__unionname: "FooBarUnion" +type: Object.Foo +| Object.Bar +} }" `; From 75ae3b54e05553bb9a86beeb9ddc001ef905ec2b Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 10:01:42 -0500 Subject: [PATCH 27/71] alias support --- src/ResultSet/ResultSet.test-d.ts | 5 ++++ src/ResultSet/ResultSet.ts | 42 ++++++++++++++----------------- src/SelectionSet/SelectionSet.ts | 19 +++++++++++--- src/lib/prelude.ts | 6 ++++- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index c8036602e..66a416267 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -37,6 +37,11 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | {} | { id: null|string } }>() expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | { __typename: "Bar" } | { __typename: "Foo"; id: null|string } }>() + // Alias + expectTypeOf>().toEqualTypeOf<{ id2: null | string }>() + // Interface + // Directive + // Arguments // scalar expectTypeOf>().toEqualTypeOf<{ stringWithArgs: null | string }>() diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index 62bb56cc9..1795bd7ec 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -1,14 +1,10 @@ /* eslint-disable @typescript-eslint/ban-types */ -import type { GetKeyOr, Values } from '../lib/prelude.js' +import type { GetKeyOr, SimplifyDeep, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/__.js' import type { SelectionSet } from '../SelectionSet/__.js' -import type { ConditionalSimplifyDeep } from 'type-fest/source/conditional-simplify.js' - -type SimplifyDeep = ConditionalSimplifyDeep | Date, object> // eslint-disable-line - // dprint-ignore export type Query<$SelectionSetQuery extends object, $Index extends Schema.Index> = SimplifyDeep, $Index>> @@ -21,15 +17,9 @@ export type Mutation<$SelectionSetMutation extends object, $Index extends Schema export type Subscription<$SelectionSetSubscription extends object, $Index extends Schema.Index> = SimplifyDeep, $Index>> -// // dprint-ignore -// export type ResultSet<$SelectionSet extends object, $Node extends Schema.Node> = -// $Node extends Schema.Nullable -// ? null | ResultSet<$SelectionSet, Exclude<$Node, Schema.Nullable>> -// : ResultSetObject<$SelectionSet, $Node> - // dprint-ignore type Node<$SelectionSet, $Node extends Schema.Node, $Index extends Schema.Index> = - $Node extends Schema.Union ? SimplifyDeep> : + $Node extends Schema.Union ? Union<$SelectionSet, $Node, $Index> : $Node extends Schema.Object ? Object<$SelectionSet, $Node, $Index> : $Node extends Schema.Scalar ? $Node : Errors.UnknownNode<$Node> @@ -37,16 +27,22 @@ type Node<$SelectionSet, $Node extends Schema.Node, $Index extends Schema.Index> // dprint-ignore export type Object<$SelectionSet, $Object extends Schema.Object, $Index extends Schema.Index> = SelectionSet.IsSelectScalarsWildcard<$SelectionSet> extends true - ? { - [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: - Field<$SelectionSet, Schema.AsField<$Object[$Key]>, $Index> - } - : { - [$SSKey in keyof $SelectionSet & string as $SelectionSet[$SSKey] extends SelectionSet.ClientIndicatorNegative ? never : $SSKey]: - $SSKey extends keyof $Object - ? SimplifyDeep, $Index>> - : Errors.UnknownFieldName<$SSKey, $Object> - } + /** + * Handle Scalars Wildcard + */ + ? { + [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: + Field<$SelectionSet, Schema.AsField<$Object[$Key]>, $Index> + } + /** + * Handle fields in regular way. + */ + : SelectionSet.ResolveAliasTargets<{ + [K in keyof SelectionSet.OmitNegativeIndicators<$SelectionSet> & string as K extends `${K}_as_${infer s}` ? s : K]: + SimplifyDeep extends keyof $Object + ? Field<$SelectionSet[K], Schema.AsField<$Object[SelectionSet.AliasNameOrigin]>, $Index> + : Errors.UnknownFieldName> + }> // dprint-ignore type Union<$SelectionSet, $Node extends Schema.Union, $Index extends Schema.Index> = @@ -68,7 +64,7 @@ export namespace Errors { TSError<'Node', `Unknown case`, { $Node: $Node }> export type UnknownFieldName<$FieldName extends string, $Node extends Schema.Object> = - TSError<'ResultSetObject', `field "${$FieldName}" does not exist on schema object "${$Node['__typename']['type']}"`> + TSError<'Object', `field "${$FieldName}" does not exist on schema object "${$Node['__typename']['type']}"`> } // type SelectField<$Objekt extends Schema.Object, $SelectionObjekt extends SelectionObjekt, $FieldName extends keyof $SelectionObjekt & string> = diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 57914fe4c..4b3799b49 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/ban-types */ -import type { MaybeList, NonEmptyString, Values } from '../lib/prelude.js' +import { O } from 'vitest/dist/reporters-QGe8gs4b.js' +import type { MaybeList, StringNonEmpty, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/__.js' @@ -31,7 +32,7 @@ type SelectionSetObject< * Allow every field to also be given as a key with this pattern `_as_: ...` */ { [ - Key in keyof $Object as `${keyof $Object & string}_as_${NonEmptyString}` + Key in keyof $Object as `${keyof $Object & string}_as_${StringNonEmpty}` ]?: SelectionSetField< Schema.AsField<$Object[Key]>, $Index @@ -103,7 +104,19 @@ export type UnionExtractFragmentNames = Values< } > export type UnionOmitFragments = { - [$K in keyof T as $K extends `on${NonEmptyString}` ? never : $K]: T[$K] + [$K in keyof T as $K extends `on${StringNonEmpty}` ? never : $K]: T[$K] +} + +export type OmitNegativeIndicators<$SelectionSet> = { + [K in keyof $SelectionSet as $SelectionSet[K] extends ClientIndicatorNegative ? never : K]: $SelectionSet[K] +} + +export type AliasNameOrigin = N extends `${infer O}_as_${StringNonEmpty}` ? O : N + +export type AliasNameTarget = N extends `${StringNonEmpty}_as_${infer T}` ? T : N + +export type ResolveAliasTargets = { + [K in keyof T as AliasNameTarget]: T[K] } /** diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index 4155b4e3a..c6e820587 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -148,7 +148,7 @@ export type Character = | 'Y' | 'Z' -export type NonEmptyString = `${Character}${string}` +export type StringNonEmpty = `${Character}${string}` export type MaybeList = T | T[] @@ -157,3 +157,7 @@ export type NotEmptyObject = keyof T extends never ? never : T export type Values = T[keyof T] export type GetKeyOr = Key extends keyof T ? T[Key] : Or + +import type { ConditionalSimplifyDeep } from 'type-fest/source/conditional-simplify.js' + +export type SimplifyDeep = ConditionalSimplifyDeep | Date, object> // eslint-disable-line From 35d26ffd87e252e17d4a0e4cbc225a9a700633ea Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 11:09:06 -0500 Subject: [PATCH 28/71] stricter alias parsing --- src/ResultSet/ResultSet.test-d.ts | 10 +++++++++- src/SelectionSet/SelectionSet.test-d.ts | 15 ++++++++++++--- src/SelectionSet/SelectionSet.ts | 21 ++++++++++++++++----- src/lib/prelude.ts | 7 +++++-- src/schema/Schema.test-d.ts | 16 ++++++++++++++++ src/schema/Schema.ts | 19 +++++++++++++++++++ 6 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 src/schema/Schema.test-d.ts diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 66a416267..358eac403 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -37,9 +37,17 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | {} | { id: null|string } }>() expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | { __typename: "Bar" } | { __typename: "Foo"; id: null|string } }>() + // Interface + // todo + // Alias + // scalar expectTypeOf>().toEqualTypeOf<{ id2: null | string }>() - // Interface + expectTypeOf>().toEqualTypeOf<{ id_as: ResultSet.Errors.UnknownFieldName<'id_as', Schema.Root.Query> }>() + expectTypeOf>().toEqualTypeOf<{ id_as_$: ResultSet.Errors.UnknownFieldName<'id_as_$', Schema.Root.Query> }>() + // union fragment + expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | {} | { id2: null|string } }>() + // Directive // Arguments diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index e2b4499b5..0086121c4 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -1,8 +1,17 @@ -import { assertType, test } from 'vitest' +import { assertType, expectTypeOf, test } from 'vitest' import type * as Schema from '../../tests/builder/_/schema.js' -import type { Query } from './SelectionSet.js' +import type { SelectionSet } from './__.js' -type S = Query +type S = SelectionSet.Query + +test(`Alias`, () => { + expectTypeOf>().toEqualTypeOf>() + expectTypeOf>().toEqualTypeOf<'a'>() + expectTypeOf>().toEqualTypeOf<'$'>() + expectTypeOf>().toEqualTypeOf<'a_as_$'>() + expectTypeOf>().toEqualTypeOf<'$_as_b'>() + expectTypeOf>().toEqualTypeOf<'__as__'>() +}) test(`general`, () => { // Scalar diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 4b3799b49..279708377 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/ban-types */ -import { O } from 'vitest/dist/reporters-QGe8gs4b.js' import type { MaybeList, StringNonEmpty, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/__.js' @@ -111,12 +110,24 @@ export type OmitNegativeIndicators<$SelectionSet> = { [K in keyof $SelectionSet as $SelectionSet[K] extends ClientIndicatorNegative ? never : K]: $SelectionSet[K] } -export type AliasNameOrigin = N extends `${infer O}_as_${StringNonEmpty}` ? O : N +export interface Alias { + origin: O + target: T +} + +// dprint-ignore +export type ParseAliasExpression = + E extends `${infer O}_as_${infer T}` ? Schema.NameParse extends never ? E : + Schema.NameParse extends never ? E : + Alias + : E + +export type AliasNameOrigin = ParseAliasExpression extends Alias ? O : N -export type AliasNameTarget = N extends `${StringNonEmpty}_as_${infer T}` ? T : N +export type AliasNameTarget = ParseAliasExpression extends Alias ? T : N -export type ResolveAliasTargets = { - [K in keyof T as AliasNameTarget]: T[K] +export type ResolveAliasTargets = { + [Field in keyof SelectionSet as AliasNameTarget]: SelectionSet[Field] } /** diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index c6e820587..7f1790603 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -94,7 +94,9 @@ export type Exact = export type Narrowable = string | number | bigint | boolean | [] -export type Character = +export type Letter = LetterLower | LetterUpper + +export type LetterLower = | 'a' | 'b' | 'c' @@ -121,6 +123,7 @@ export type Character = | 'x' | 'y' | 'z' +export type LetterUpper = | 'A' | 'B' | 'C' @@ -148,7 +151,7 @@ export type Character = | 'Y' | 'Z' -export type StringNonEmpty = `${Character}${string}` +export type StringNonEmpty = `${Letter}${string}` export type MaybeList = T | T[] diff --git a/src/schema/Schema.test-d.ts b/src/schema/Schema.test-d.ts new file mode 100644 index 000000000..b9c6adbfc --- /dev/null +++ b/src/schema/Schema.test-d.ts @@ -0,0 +1,16 @@ +import { expectTypeOf, test } from 'vitest' +import type { Schema } from './__.js' + +test(`NameParse`, () => { + expectTypeOf>().toEqualTypeOf<'a'>() + expectTypeOf>().toEqualTypeOf<'A'>() + expectTypeOf>().toEqualTypeOf<'aa'>() + expectTypeOf>().toEqualTypeOf<'a_'>() + expectTypeOf>().toEqualTypeOf<'a__'>() + expectTypeOf>().toEqualTypeOf<'a__b'>() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() +}) diff --git a/src/schema/Schema.ts b/src/schema/Schema.ts index ef8708fbe..26b9e62a0 100644 --- a/src/schema/Schema.ts +++ b/src/schema/Schema.ts @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/ban-types */ +import type { Letter } from '../lib/prelude.js' + export interface Index { unions: { Union: null | Union @@ -32,3 +34,20 @@ export type Field = FieldBase export type Args = { type: object; allOptional: boolean } export type AsField = T extends Field ? T : never + +// dprint-ignore +export type NameParse = + T extends NameHead ? T : + T extends `${NameHead}${infer Rest}` ? Rest extends NameBodyParse ? T + : never + : never + +// dprint-ignore +export type NameBodyParse = + S extends NameBody ? S : + S extends `${NameBody}${infer Rest}` ? NameBodyParse extends string ? S + : never + : never + +export type NameHead = Letter +export type NameBody = Letter | '_' From 1340a946bb2ed3f781749e5dfa194eb343b67d4c Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 11:14:08 -0500 Subject: [PATCH 29/71] tidy --- src/SelectionSet/SelectionSet.test-d.ts | 4 ++-- src/SelectionSet/SelectionSet.ts | 26 ++++++++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 0086121c4..cd3e12edb 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -4,7 +4,7 @@ import type { SelectionSet } from './__.js' type S = SelectionSet.Query -test(`Alias`, () => { +test(`ParseAliasExpression`, () => { expectTypeOf>().toEqualTypeOf>() expectTypeOf>().toEqualTypeOf<'a'>() expectTypeOf>().toEqualTypeOf<'$'>() @@ -13,7 +13,7 @@ test(`Alias`, () => { expectTypeOf>().toEqualTypeOf<'__as__'>() }) -test(`general`, () => { +test(`Query`, () => { // Scalar assertType({ id: true }) assertType({ id: false }) diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 279708377..c173fb025 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -90,11 +90,8 @@ type SelectionSetUnion< */ /** - * Should this field be selected? + * Unions */ -export type ClientIndicator = ClientIndicatorPositive | ClientIndicatorNegative -export type ClientIndicatorPositive = true | 1 -export type ClientIndicatorNegative = false | 0 | undefined export type UnionFragmentExtractName = T extends `on${infer $Name}` ? $Name : never export type UnionExtractFragmentNames = Values< @@ -106,9 +103,9 @@ export type UnionOmitFragments = { [$K in keyof T as $K extends `on${StringNonEmpty}` ? never : $K]: T[$K] } -export type OmitNegativeIndicators<$SelectionSet> = { - [K in keyof $SelectionSet as $SelectionSet[K] extends ClientIndicatorNegative ? never : K]: $SelectionSet[K] -} +/** + * Aliases + */ export interface Alias { origin: O @@ -130,6 +127,21 @@ export type ResolveAliasTargets = { [Field in keyof SelectionSet as AliasNameTarget]: SelectionSet[Field] } +/** + * Indicators + */ + +/** + * Should this field be selected? + */ +export type ClientIndicator = ClientIndicatorPositive | ClientIndicatorNegative +export type ClientIndicatorPositive = true | 1 +export type ClientIndicatorNegative = false | 0 | undefined + +export type OmitNegativeIndicators<$SelectionSet> = { + [K in keyof $SelectionSet as $SelectionSet[K] extends ClientIndicatorNegative ? never : K]: $SelectionSet[K] +} + /** * Field selection in general, with directives support too. * If a field directive is given as an indicator then it implies "select this" e.g. `true`/`1`. From 388eea470e86bf762d03f6a9cae13434b05bbcd9 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 12:04:43 -0500 Subject: [PATCH 30/71] tidy --- src/ResultSet/ResultSet.test-d.ts | 102 +++++++----------------- src/ResultSet/ResultSet.ts | 50 ++++++++---- src/SelectionSet/SelectionSet.test-d.ts | 4 +- src/SelectionSet/SelectionSet.ts | 17 ++++ 4 files changed, 85 insertions(+), 88 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 358eac403..9f4529174 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/ban-types */ + import { expectTypeOf, test } from 'vitest' import type * as Schema from '../../tests/builder/_/schema.js' import type { SelectionSet } from '../SelectionSet/__.js' @@ -43,12 +44,40 @@ test(`general`, () => { // Alias // scalar expectTypeOf>().toEqualTypeOf<{ id2: null | string }>() + expectTypeOf>().toEqualTypeOf<{ id2: string }>() expectTypeOf>().toEqualTypeOf<{ id_as: ResultSet.Errors.UnknownFieldName<'id_as', Schema.Root.Query> }>() expectTypeOf>().toEqualTypeOf<{ id_as_$: ResultSet.Errors.UnknownFieldName<'id_as_$', Schema.Root.Query> }>() // union fragment expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | {} | { id2: null|string } }>() - // Directive + // Directive on scalar non-nullable + // include + expectTypeOf>().toEqualTypeOf<{ idNonNull: null|string }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: null|string }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: null }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: null }>() + // skip + expectTypeOf>().toEqualTypeOf<{ idNonNull: null|string }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: null|string }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: null }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: null }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() + expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() + // Directive on scalar nullable + // include + expectTypeOf>().toEqualTypeOf<{ id: null|string }>() + expectTypeOf>().toEqualTypeOf<{ id: null }>() + expectTypeOf>().toEqualTypeOf<{ id: null|string }>() + // skip + expectTypeOf>().toEqualTypeOf<{ id: null|string }>() + expectTypeOf>().toEqualTypeOf<{ id: null|string }>() + expectTypeOf>().toEqualTypeOf<{ id: null }>() + + + // Field Group + // todo // Arguments // scalar @@ -58,75 +87,4 @@ test(`general`, () => { // Errors // unknown field expectTypeOf>().toEqualTypeOf<{ id2: ResultSet.Errors.UnknownFieldName<'id2', Schema.Root.Query> }>() - - // assertType>({string:''}) }) -// import { describe, expectTypeOf, test } from 'vitest' -// import type { TSError } from '../../../src/lib/TSError.js' -// // import type { TSError } from '~/lib/prelude/index.js' -// // import type * as GenqlTypes from '../genql/schema.js' -// // import type { SelectQuery } from './inferSelectionResult.js' - -// test(`utilities`, () => { -// // expectTypeOf>().toEqualTypeOf<{__typename:true}|{message:true}>() // prettier-ignore -// }) - -// // dprint-ignore -// describe(`direct`, () => { -// test(`interface`, () => { -// expectTypeOf>().toEqualTypeOf<{__typename: 'OfferAbstract' | 'OfferValue' | 'OfferResourceProperty' | 'OfferResourceAggregation'}>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ id: string }>() -// expectTypeOf>().toEqualTypeOf<{} | { id: string }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{}|{id:string}>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{}|{feature:{id:string}}>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{}|{__typename:'OfferAbstract'}>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{}|{__typename:'OfferAbstract'}|{__typename:'OfferValue'}>() // prettier-ignore -// // expectTypeOf<>().toEqualTypeOf<>() // prettier-ignore -// }) -// }) - -// // dprint-ignore -// describe(`Query`, () => { -// test(`object`, () => { -// expectTypeOf>().toEqualTypeOf<{}>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ me: { __typename: 'Me' } }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ me: { user: { id: string } } }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ me: { user: { __typename: 'User'; id: string; displayName: string | null; email: string; handle: string | null; image: string | null } } }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ me: { user: {} } }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ me: {} }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ me: {} }>() // prettier-ignore -// }) -// test(`object list`, () => { -// expectTypeOf>().toEqualTypeOf<{ me: { workspaces: { id: string }[] } }>() // prettier-ignore -// }) -// test(`scalar list`, () => { -// expectTypeOf>().toEqualTypeOf<{ plan: {}|{offers:{platform:{support:{limit:{allowed:string[]}|{}|null}}}}}>() // prettier-ignore -// }) - -// test(`union`, () => { -// expectTypeOf>().toEqualTypeOf<{ project: | { __typename: 'Project' } | { __typename: 'ErrorInternal' } | { __typename: 'ErrorUserBusinessNotAuthorized' } | { __typename: 'ErrorUserBusinessResourceNotFound' } }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ project: | { __typename: 'Project'; id: string } | { __typename: 'ErrorInternal' } | { __typename: 'ErrorUserBusinessNotAuthorized' } | { __typename: 'ErrorUserBusinessResourceNotFound' } }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ project: {} | { id: string } }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ project: {} | { message: string } | { id: string } }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ project: {} | { id: string } }>() // prettier-ignore -// expectTypeOf>().toMatchTypeOf<{ project: {} }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ project: {} | { id: string } }>() // prettier-ignore -// // expectTypeOf<>().toEqualTypeOf<>() // prettier-ignore -// }) -// test(`custom scalar`, () => { -// expectTypeOf>().toEqualTypeOf<{ project: {} | { createdAt: string } }>() // prettier-ignore -// }) -// test(`interface`, () => { -// // expectTypeOf<>().toEqualTypeOf<>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ project: {} | { message: string }}>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ project: { message: string } | { id: string }}>() // prettier-ignore -// }) -// test(`nullable`, () => { -// expectTypeOf>().toEqualTypeOf<{ project: {} | { pulse: { databaseLink: {} | null } } }>() // prettier-ignore -// }) -// test(`errors`, () => { -// // expectTypeOf<>().toEqualTypeOf<>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ foo: TSError<'SelectObjektField', 'object selection field "foo" does not exist on schema object "Query"'> }>() // prettier-ignore -// expectTypeOf>().toEqualTypeOf<{ me: { bar: TSError<'SelectObjektField', 'object selection field "bar" does not exist on schema object "Me"'> } }>() // prettier-ignore -// }) -// }) diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index 1795bd7ec..abd8dbf6a 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -30,19 +30,22 @@ export type Object<$SelectionSet, $Object extends Schema.Object, $Index extends /** * Handle Scalars Wildcard */ - ? { - [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: - Field<$SelectionSet, Schema.AsField<$Object[$Key]>, $Index> + ? + { + [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: + Field<$SelectionSet, Schema.AsField<$Object[$Key]>, $Index> } - /** - * Handle fields in regular way. - */ - : SelectionSet.ResolveAliasTargets<{ - [K in keyof SelectionSet.OmitNegativeIndicators<$SelectionSet> & string as K extends `${K}_as_${infer s}` ? s : K]: - SimplifyDeep extends keyof $Object - ? Field<$SelectionSet[K], Schema.AsField<$Object[SelectionSet.AliasNameOrigin]>, $Index> - : Errors.UnknownFieldName> - }> + : ( + /** + * Handle fields in regular way. + */ + | SelectionSet.ResolveAliasTargets<{ + [K in keyof SelectionSet.OmitNegativeIndicators<$SelectionSet> & string as K extends `${K}_as_${infer s}` ? s : K]: + SimplifyDeep extends keyof $Object + ? Field<$SelectionSet[K], Schema.AsField<$Object[SelectionSet.AliasNameOrigin]>, $Index> + : Errors.UnknownFieldName> + }> + ) // dprint-ignore type Union<$SelectionSet, $Node extends Schema.Union, $Index extends Schema.Index> = @@ -53,8 +56,27 @@ type Union<$SelectionSet, $Node extends Schema.Union, $Index extends Schema.Inde // dprint-ignore type Field<$SelectionSet, $Field extends Schema.Field, $Index extends Schema.Index> = - | FieldNullable<$Field> - | Node<$SelectionSet, $Field['type'], $Index> + $SelectionSet extends SelectionSet.Directive.Include.Negative | SelectionSet.Directive.Skip.Positive ? null : + ( + | FieldNullable<$Field> + | FieldDirectiveInclude<$SelectionSet> + | FieldDirectiveSkip<$SelectionSet> + | Node<$SelectionSet, $Field['type'], $Index> + ) + +// dprint-ignore +type FieldDirectiveInclude<$SelectionSet> = + $SelectionSet extends SelectionSet.Directive.Include ? $SelectionSet extends SelectionSet.Directive.Include.Positive ? + never : + null + : never + +// dprint-ignore +type FieldDirectiveSkip<$SelectionSet> = + $SelectionSet extends SelectionSet.Directive.Skip ? $SelectionSet extends SelectionSet.Directive.Skip.Negative ? + never : + null + : never type FieldNullable<$Field extends Schema.Field> = $Field['nullable'] extends true ? null : never diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index cd3e12edb..316cfe0a6 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -97,7 +97,7 @@ test(`Query`, () => { assertType({ string: { $stream: { if: true } } }) assertType({ string: { $stream: {} } }) - // group of fields + // Field Group assertType({ object: { ___: { $skip: true, int: true, id: true } } }) assertType({ object: { ___: [{ $skip: true, int: true, id: true }] } }) @@ -144,7 +144,7 @@ test(`Query`, () => { // @ts-expect-error missing args ("$") assertType({ stringWithRequiredArg: {} }) - // all-scalars "client directive" + // Scalars Wildcard ("client directive") // object assertType({ object: { $scalars: true } }) // @ts-expect-error no directives on scalars field diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index c173fb025..b34e617b3 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -127,6 +127,23 @@ export type ResolveAliasTargets = { [Field in keyof SelectionSet as AliasNameTarget]: SelectionSet[Field] } +/** + * Directives + */ + +export namespace Directive { + export type Include = { $include: boolean | { if: boolean } } + export namespace Include { + export type Positive = { $include: true | { if: true } } + export type Negative = { $include: false | { if: false } } + } + export type Skip = { $skip: boolean | { if: boolean } } + export namespace Skip { + export type Positive = { $skip: true | { if: true } } + export type Negative = { $skip: false | { if: false } } + } +} + /** * Indicators */ From 830c1e6b012d54cef1887a315ddedab52ec576aa Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 12:07:18 -0500 Subject: [PATCH 31/71] organize todos --- src/ResultSet/ResultSet.test-d.ts | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 9f4529174..fca59b671 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -41,6 +41,9 @@ test(`general`, () => { // Interface // todo + // List + // todo + // Alias // scalar expectTypeOf>().toEqualTypeOf<{ id2: null | string }>() @@ -50,31 +53,37 @@ test(`general`, () => { // union fragment expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | {} | { id2: null|string } }>() - // Directive on scalar non-nullable - // include + // Directive @include + // On scalar non-nullable expectTypeOf>().toEqualTypeOf<{ idNonNull: null|string }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: null|string }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: null }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: null }>() - // skip + // On scalar nullable + expectTypeOf>().toEqualTypeOf<{ id: null|string }>() + expectTypeOf>().toEqualTypeOf<{ id: null }>() + expectTypeOf>().toEqualTypeOf<{ id: null|string }>() + + // Directive @skip + // On scalar non-nullable expectTypeOf>().toEqualTypeOf<{ idNonNull: null|string }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: null|string }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: null }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: null }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() expectTypeOf>().toEqualTypeOf<{ idNonNull: string }>() - // Directive on scalar nullable - // include - expectTypeOf>().toEqualTypeOf<{ id: null|string }>() - expectTypeOf>().toEqualTypeOf<{ id: null }>() - expectTypeOf>().toEqualTypeOf<{ id: null|string }>() - // skip + // On scalar nullable expectTypeOf>().toEqualTypeOf<{ id: null|string }>() expectTypeOf>().toEqualTypeOf<{ id: null|string }>() expectTypeOf>().toEqualTypeOf<{ id: null }>() + // Directive @defer + // todo + + // Directive @stream + // todo // Field Group // todo From 140f49e36bea1c7c38f5071f58b2e71ef1113f6d Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 12:07:59 -0500 Subject: [PATCH 32/71] ws --- src/ResultSet/ResultSet.test-d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index fca59b671..d977ee3e0 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -20,7 +20,7 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ id: null | string }>() expectTypeOf>().toEqualTypeOf<{ id: null | string }>() expectTypeOf>().toEqualTypeOf<{ id: null | string }>() - + // Object expectTypeOf>().toEqualTypeOf<{ object: null | { id: string | null } }>() // non-nullable @@ -31,7 +31,7 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ objectNested: null | { __typename: "ObjectNested"; id: null|string } }>() // __typename expectTypeOf>().toEqualTypeOf<{ objectNonNull: { __typename: "Object" } }>() - + // Union expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | { __typename: "Foo" } | { __typename: "Bar" } }>() expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | {} | { __typename: "Foo" } }>() From 7f449c1e998edda24f12cb7b925fe55014c9ee43 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 16:26:30 -0500 Subject: [PATCH 33/71] refactor gen --- src/ResultSet/ResultSetReference.ts | 183 ------------------ src/generator/generator.ts | 168 +++++++--------- src/lib/Code.ts | 12 +- tests/builder/_/schema.graphql | 3 + tests/builder/_/schema.ts | 22 ++- .../__snapshots__/generate.test.ts.snap | 22 ++- 6 files changed, 121 insertions(+), 289 deletions(-) delete mode 100644 src/ResultSet/ResultSetReference.ts diff --git a/src/ResultSet/ResultSetReference.ts b/src/ResultSet/ResultSetReference.ts deleted file mode 100644 index ee796842d..000000000 --- a/src/ResultSet/ResultSetReference.ts +++ /dev/null @@ -1,183 +0,0 @@ -// /* eslint-disable @typescript-eslint/ban-types */ - -// /** -// * The follow types work around limitations in the GenQL types with regards to union types. -// * @see https://github.com/remorses/genql/issues/108 -// */ -// import type { UnionToIntersection } from 'type-fest' -// import type { ConditionalSimplifyDeep } from 'type-fest/source/conditional-simplify' -// import type { Values } from '~/lib/prelude' -// import { type TSError, type ValuesOrEmptyObject } from '~/lib/prelude' -// import type * as GenqlTypes from '../genql/schema.js' -// import type { FieldNameConstants, Interface, List, Node, Nullable, Objekt, Scalar } from '../types' -// import type { GetSelection } from './utilities.js' - -// type SimplifyDeep = ConditionalSimplifyDeep | Date, object> // eslint-disable-line - -// type SelectionRoot = SelectionObjekt -// type SelectionScalar = boolean -// type SelectionObjekt = object - -// export type SelectMutation<$SelectionRoot extends SelectionRoot> = Select<'Mutation', $SelectionRoot> - -// export type SelectQuery<$SelectionRoot extends SelectionRoot> = Select<'Query', $SelectionRoot> - -// export type Selection< -// $TypeName extends keyof GenqlTypes.Selections, -// $SelectionObjekt extends GetSelection<$TypeName>, -// > = $SelectionObjekt - -// export type Select< -// $TypeName extends keyof GenqlTypes.Selections, -// $SelectionObjekt extends GetSelection<$TypeName>, -// > // @ts-expect-error fixme -// = SimplifyDeep> - -// export type SelectOn< -// $Node extends Objekt, -// $SelectionObjekt extends GetSelection<$Node['__typename']>, -// > = SimplifyDeep> - -// // dprint-ignore -// type SelectObjekt<$Node extends Node, $Selection> = -// $Selection extends object ? $Node extends Nullable ? null | SelectObjekt, $Selection> -// : $Node extends Scalar ? TSError<'SelectObjekt','$Node is Scalar (should be Object).'> -// : $Node extends List ? TSError<'SelectObjekt','$Node is List (should be Object).'> -// : $Node extends List ? SelectObjekt_<$Node[number], $Selection>[] -// // : $Node extends Interface ? SelectInterface<$Node, $Selection> -// : $Node extends Objekt ? SelectObjekt_<$Node, $Selection> -// : TSError<'SelectObjekt','$Node is unknown type (should be Objekt).', { $Node: $Node }> -// : TSError<'SelectObjekt','$Selection is not an object.', { $Selection: $Selection }> - -// // dprint-ignore -// type SelectObjekt_<$Node extends Objekt, $Selection extends SelectionObjekt> = - -// /** -// * Nothing to do, the selection set is empty (e.g.: \{\} ) -// */ -// keyof $Selection extends never ? {} -// : -// /** -// * Handle "regular" selection set fields (not a union or interface) -// */ -// { -// [ -// $Field in keyof $Selection & string as -// $Field extends FieldNameConstants.__scalar ? never -// : $Field extends FieldNameConstants.__args ? never -// : $Field extends `on_${infer _}` ? never -// : $Selection[$Field] extends false ? never -// : $Field -// ]: -// SelectObjektField<$Node, $Selection, $Field> -// } - -// & - -// /** -// * Handle the __scalar: true feature. This special field selector means -// * select _all_ scalar fields of the object. -// */ -// ( -// IsSelectsField extends true -// ? { -// [$Field in keyof $Node & string as $Node[$Field] extends Scalar | Nullable ? $Field : never]: $Node[$Field] -// } -// : {} // eslint-disable-line -// ) - -// & - -// /** -// * Handle selection sets for interfaces and union type members. -// * -// * @remarks This selection set needs to be "hoisted" into the parent selection set. -// * Hence the TypeScript intersection ("&") above. -// */ -// UnionToIntersection -// : $InterfaceOrObjectTypeName extends keyof GenqlTypes.Objects ? SelectObjekt -// : TSError<'SelectObjekt_', `The given type name in on_TYPE_NAME of ${$InterfaceOrObjectTypeName} does not correspond to any know object or interface name.`> -// : never -// }>> - -// & - -// ( -// $Node extends Interface ? -// // FieldNameConstants.__typenameInterface extends keyof $Node ? -// // $Node[FieldNameConstants.__typenameInterface] extends keyof GenqlTypes.Interfaces.$Guide ? -// // Values extends Objekt ? -// // @ts-expect-error fixme -// SelectAgainstInterface, $Selection> -// : {} -// // : never -// // : never -// // : never -// ) - -// // dprint-ignore -// type SelectAgainstInterface< -// $Implementor extends Objekt, -// $Selection, -// > = -// $Implementor extends any ? // force distribution over union type -// `on_${$Implementor['__typename']}` extends keyof $Selection -// ? SelectObjekt<$Implementor, $Selection[`on_${$Implementor['__typename']}`]> -// : {} -// : never - -// // dprint-ignore -// type SelectObjektField<$Objekt extends Objekt, $SelectionObjekt extends SelectionObjekt, $Field extends keyof $SelectionObjekt & string> = -// $Field extends keyof $Objekt ? $Objekt[$Field] extends Node ? $SelectionObjekt[$Field] extends SelectionScalar ? SelectScalar<$Objekt[$Field], $SelectionObjekt[$Field]> -// : $SelectionObjekt[$Field] extends SelectionObjekt ? SelectObjekt<$Objekt[$Field], $SelectionObjekt[$Field]> -// : TSError<'SelectionObjektField', `selection object field "${$Field}" is of unknown type.`> -// : [$SelectionObjekt,$Field,$Objekt[$Field],TSError<'SelectObjektField', `object selection field "${$Field}" on schema object "${$Objekt['__typename']}" is not a Node.`>] -// // : $Field extends `on_${infer $TypeName}` ? TSError<'SelectObjektField', `fragment field "on_${$TypeName}" should be handled by caller.`> -// : TSError<'SelectObjektField', `object selection field "${$Field}" does not exist on schema object "${$Objekt['__typename']}"`> - -// // dprint-ignore -// type SelectScalar<$Node extends Node, $SelectionScalar extends SelectionScalar> = -// $Node extends Nullable ? null | SelectScalar, $SelectionScalar> -// : $Node extends List ? SelectScalar<$Node[number], $SelectionScalar>[] -// : $Node extends Objekt ? TSError<'SelectScalar','$Node is Objekt.'> -// : $Node extends Scalar ? $SelectionScalar extends true ? $Node -// : never -// : TSError<'SelectScalar','$Node is of unknown type.'> - -// type IsSelectsField< -// $Field extends string, -// $SelectionObjekt extends SelectionObjekt, -// > = $Field extends keyof $SelectionObjekt ? ($SelectionObjekt[$Field] extends true ? true : false) : false - -// // // dprint-ignore -// // type GetInterfaceImplementorSelections<$Interface extends Interface, $Selection> = -// // $Selection[GetInterfaceImplementorSelections_<$Interface, keyof $Selection & string>] -// // // dprint-ignore -// // type GetInterfaceImplementorSelections_<$Interface extends Interface, $SelectionField extends string> = -// // $SelectionField extends `on_${infer _ extends keyof GenqlTypes.Interfaces.$Guide[$Interface[FieldNameConstants.__typenameInterface]]['implementors'] & string}` -// // ? $SelectionField -// // : never diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 845ac4795..befba7286 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -6,16 +6,7 @@ import type { GraphQLInterfaceType, GraphQLNamedType, } from 'graphql' -import { - GraphQLNonNull, - GraphQLObjectType, - isEnumType, - isInterfaceType, - isNamedType, - isObjectType, - isScalarType, - isUnionType, -} from 'graphql' +import { GraphQLNonNull, GraphQLObjectType, isEnumType, isNamedType } from 'graphql' import { buildSchema } from 'graphql' import _ from 'json-bigint' import fs from 'node:fs/promises' @@ -44,7 +35,7 @@ type AnyGraphQLFieldsType = | GraphQLInterfaceType | GraphQLInputObjectType -const definePointerRenderers = < +const defineReferenceRenderers = < $Renderers extends { [ClassName in keyof NameToClass]: any }, >( renderers: { @@ -85,7 +76,7 @@ const defineConcreteRenderers = < ) as any } -const dispatchToPointerRenderer = (config: Config, node: AnyClass): string => { +const dispatchToReferenceRenderer = (config: Config, node: AnyClass): string => { // @ts-expect-error lookup const renderer = pointerRenderers[node.constructor.name] // eslint-disable-line if (!renderer) { @@ -106,12 +97,12 @@ const dispatchToConcreteRenderer = ( return renderer(config, node) // eslint-disable-line } -const pointerRenderers = definePointerRenderers({ - GraphQLNonNull: (config, node) => dispatchToPointerRenderer(config, node.ofType), +const pointerRenderers = defineReferenceRenderers({ + GraphQLNonNull: (config, node) => dispatchToReferenceRenderer(config, node.ofType), GraphQLEnumType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLEnumType, node.name), GraphQLInputObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInputObjectType, node.name), GraphQLInterfaceType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), - GraphQLList: (config, node) => Code.list(dispatchToPointerRenderer(config, node.ofType)), + GraphQLList: (config, node) => Code.list(dispatchToReferenceRenderer(config, node.ofType)), GraphQLObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), GraphQLScalarType: (_, node) => `$.Scalars[${Code.quote(node.name)}]`, GraphQLUnionType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), @@ -150,19 +141,18 @@ const concreteRenderers = defineConcreteRenderers({ Code.export$( Code.interface$( node.name, - Code.fields([ - Code.field(`__unionname`, Code.quote(node.name)), - Code.field( - `type`, - Code.unionItems( + Code.objectFrom({ + __unionname: { type: Code.quote(node.name) }, + type: { + type: Code.unionItems( node .getTypes() .map( - (_) => dispatchToPointerRenderer(config, _), + (_) => dispatchToReferenceRenderer(config, _), ), ), - ), - ]), + }, + }), ), ), ), @@ -223,15 +213,15 @@ const renderFields = (config: Config, node: AnyGraphQLFieldsType): string => { ? [ Code.field( `__typename`, - Code.object(Code.fields([ - Code.field(`type`, `"${node.name}"`), - Code.field(`nullable`, `false`), - Code.field(`args`, `null`), - ])), + Code.objectFrom({ + type: { type: Code.quote(node.name) }, + nullable: { type: false }, + args: { type: null }, + }), ), ] : [] - return Code.fields([ + return Code.object(Code.fields([ ...__typenameField, ...values(node.getFields()).map((field) => Code.TSDoc( @@ -239,13 +229,13 @@ const renderFields = (config: Config, node: AnyGraphQLFieldsType): string => { Code.field(field.name, renderField(config, field)), ) ), - ]) + ])) } const renderField = (config: Config, field: AnyField): string => { const { node, nullable } = unwrapNonNull(field.type) - const type = dispatchToPointerRenderer(config, node) + const type = dispatchToReferenceRenderer(config, node) const args = isGraphQLOutputField(field) && field.args.length > 0 ? renderArgs(config, field.args) @@ -253,13 +243,12 @@ const renderField = (config: Config, field: AnyField): string => { // const fieldWithArgs = args ? Code.intersection(fieldBase, args) : fieldBase const name = isNamedType(node) ? node.name : null - return Code.object(Code.fields([ - Code.field(`type`, type), - // todo keep type name on type node instead - Code.field(`typeName`, name ? Code.quote(name) : `null`), - Code.field(`nullable`, nullable ? `true` : `false`), - Code.field(`args`, args ?? `null`), - ])) + return Code.objectFrom({ + type: { type }, + typeName: { type: name ? Code.quote(name) : `null` }, + nullable: { type: nullable ? `true` : `false` }, + args: { type: args ?? `null` }, + }) } const renderArgs = (config: Config, args: readonly GraphQLArgument[]) => { @@ -272,19 +261,17 @@ const renderArgs = (config: Config, args: readonly GraphQLArgument[]) => { return Code.field( arg.name, nullable - ? Code.nullable(dispatchToPointerRenderer(config, node)) - : dispatchToPointerRenderer(config, node), + ? Code.nullable(dispatchToReferenceRenderer(config, node)) + : dispatchToReferenceRenderer(config, node), { optional: nullable }, ) }), ), ) - return Code.object( - Code.fields([ - Code.field(`type`, argsRendered), - Code.field(`allOptional`, hasRequiredArgs ? `false` : `true`), - ]), - ) + return Code.objectFrom({ + type: { type: argsRendered }, + allOptional: { type: hasRequiredArgs ? `false` : `true` }, + }) } const unwrapNonNull = ( @@ -351,72 +338,55 @@ export const generateCode = (input: Input) => { Code.export$( Code.interface$( `Index`, - Code.fields([ - Code.field( - `Root`, - Code.object( - Code.fields([ - Code.field(`Query`, hasQuery ? `Root.Query` : `null`), - Code.field( - `Mutation`, - hasMutation ? `Root.Mutation` : `null`, - ), - Code.field( - `Subscription`, - hasSubscription ? `Root.Subscription` : `null`, - ), - ]), + Code.objectFrom({ + Root: { + type: Code.objectFrom({ + Query: { type: hasQuery ? `Root.Query` : null }, + Mutation: { type: hasMutation ? `Root.Mutation` : null }, + Subscription: { type: hasSubscription ? `Root.Subscription` : null }, + }), + }, + objects: { + type: Code.objectFromEntries( + typeMapByKind.GraphQLObjectType.map(_ => [_.name, Code.propertyAccess(`Object`, _.name)]), ), - ), - Code.field( - `objects`, - Code.object( - Code.fields( - typeMapByKind.GraphQLObjectType.map(_ => Code.field(_.name, Code.propertyAccess(`Object`, _.name))), + }, + unionMemberNames: { + type: Code.objectFromEntries( + typeMapByKind.GraphQLUnionType.map( + (_) => [_.name, Code.unionItems(_.getTypes().map(_ => Code.quote(_.name)))], ), ), - ), - Code.field( - `unionMemberNames`, - Code.object( - Code.fields(typeMapByKind.GraphQLUnionType.map( - (_) => Code.field(_.name, Code.unionItems(_.getTypes().map(_ => Code.quote(_.name)))), - )), - ), - ), - Code.field( - `unions`, - Code.object( - Code.fields([ - Code.field( - `Union`, - typeMapByKind.GraphQLUnionType.length > 0 + }, + unions: { + type: Code.objectFrom( + { + Union: { + type: typeMapByKind.GraphQLUnionType.length > 0 ? Code.unionItems( typeMapByKind.GraphQLUnionType.map( - (_) => `Union.${_.name}`, + (_) => Code.propertyAccess(`Union`, _.name), ), ) - : `null`, - ), - ]), + : null, + }, + }, ), - ), - Code.field(`scalars`, `Scalars`), - ]), + }, + scalars: { + type: `Scalars`, + }, + }), ), ), Code.export$( Code.interface$( `Scalars`, - ` - ${ - typeMapByKind.GraphQLScalarType.map((_) => { - // todo strict mode where instead of falling back to "any" we throw an error - const type = scalarTypeMap[_.name] || `string` - return Code.field(_.name, type) - }).join(`\n`) - } - `, + Code.objectFromEntries(typeMapByKind.GraphQLScalarType.map((_) => { + // todo strict mode where instead of falling back to "any" we throw an error + const type = scalarTypeMap[_.name] || `string` + return [_.name, type] + })), ), ), ), diff --git a/src/lib/Code.ts b/src/lib/Code.ts index 261f166e4..75a00bfe7 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -13,7 +13,17 @@ export namespace Code { export const fields = (fieldTypes: string[]) => fieldTypes.join(`\n`) export const intersection = (a: string, b: string) => `${a} & ${b}` export const object = (fields: string) => `{\n${fields}\n}` - export const interface$ = (name: string, fields: string) => `interface ${name} ${Code.object(fields)}` + export const objectFromEntries = (entries: [string, string][]) => + Code.objectFrom(Object.fromEntries(entries.map(([name, type]) => [name, { type }]))) + export const objectFrom = ( + object: Record, + ) => + Code.object( + Code.fields( + Object.entries(object).map(([name, spec]) => Code.field(name, String(spec.type), { optional: spec.optional })), + ), + ) + export const interface$ = (name: string, object: string) => `interface ${name} ${object}` export const export$ = (thing: string) => `export ${thing}` export const TSDoc = (content: string | null, block: string) => content === null ? block : `/**\n${prependLines(`* `, content) || `*`}\n*/\n${block}` diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index 668b5a47d..1b898e5b2 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -5,6 +5,9 @@ type Query { stringWithRequiredArg(string:String!): String stringWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): String object: Object + listListInt: [[Int!]!]! + listInt: [Int!]! + listIntNonNull: [Int!]! objectNested: ObjectNested objectNonNull: Object! objectWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): Object diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index cf38a2458..f78e89a5d 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -21,13 +21,11 @@ Union: Union.FooBarUnion scalars: Scalars } export interface Scalars { - - ID: string +ID: string String: string Int: number Float: number Boolean: boolean - } } @@ -92,6 +90,24 @@ typeName: "Object" nullable: true args: null } +listListInt: { +type: Array> +typeName: null +nullable: false +args: null +} +listInt: { +type: Array<$.Scalars["Int"]> +typeName: null +nullable: false +args: null +} +listIntNonNull: { +type: Array<$.Scalars["Int"]> +typeName: null +nullable: false +args: null +} objectNested: { type: Object.ObjectNested typeName: "ObjectNested" diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index 0e95d6bc6..35f1192da 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -24,13 +24,11 @@ Union: Union.FooBarUnion scalars: Scalars } export interface Scalars { - - ID: string +ID: string String: string Int: number Float: number Boolean: boolean - } } @@ -95,6 +93,24 @@ typeName: "Object" nullable: true args: null } +listListInt: { +type: Array> +typeName: null +nullable: false +args: null +} +listInt: { +type: Array<$.Scalars["Int"]> +typeName: null +nullable: false +args: null +} +listIntNonNull: { +type: Array<$.Scalars["Int"]> +typeName: null +nullable: false +args: null +} objectNested: { type: Object.ObjectNested typeName: "ObjectNested" From 71a81a36e9006d11ccc15956fc58aedf0f164448 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 22:04:31 -0500 Subject: [PATCH 34/71] no quotes --- src/generator/generator.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/generator/generator.ts b/src/generator/generator.ts index befba7286..7d6ca18da 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -245,9 +245,9 @@ const renderField = (config: Config, field: AnyField): string => { return Code.objectFrom({ type: { type }, - typeName: { type: name ? Code.quote(name) : `null` }, - nullable: { type: nullable ? `true` : `false` }, - args: { type: args ?? `null` }, + typeName: { type: name ? Code.quote(name) : null }, + nullable: { type: nullable ? true : false }, + args: { type: args ?? null }, }) } @@ -270,7 +270,7 @@ const renderArgs = (config: Config, args: readonly GraphQLArgument[]) => { ) return Code.objectFrom({ type: { type: argsRendered }, - allOptional: { type: hasRequiredArgs ? `false` : `true` }, + allOptional: { type: !hasRequiredArgs }, }) } From aca4fa7ce88b7f4901dfaa5a00bcb7e30142995b Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 24 Feb 2024 22:08:20 -0500 Subject: [PATCH 35/71] simplify --- src/generator/generator.ts | 2 -- tests/builder/_/schema.ts | 23 ------------------- .../__snapshots__/generate.test.ts.snap | 23 ------------------- 3 files changed, 48 deletions(-) diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 7d6ca18da..f29696254 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -241,11 +241,9 @@ const renderField = (config: Config, field: AnyField): string => { ? renderArgs(config, field.args) : null // const fieldWithArgs = args ? Code.intersection(fieldBase, args) : fieldBase - const name = isNamedType(node) ? node.name : null return Code.objectFrom({ type: { type }, - typeName: { type: name ? Code.quote(name) : null }, nullable: { type: nullable ? true : false }, args: { type: args ?? null }, }) diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index f78e89a5d..6416d998d 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -42,25 +42,21 @@ args: null } id: { type: $.Scalars["ID"] -typeName: "ID" nullable: true args: null } idNonNull: { type: $.Scalars["ID"] -typeName: "ID" nullable: false args: null } string: { type: $.Scalars["String"] -typeName: "String" nullable: true args: null } stringWithRequiredArg: { type: $.Scalars["String"] -typeName: "String" nullable: true args: { type: { @@ -71,7 +67,6 @@ allOptional: false } stringWithArgs: { type: $.Scalars["String"] -typeName: "String" nullable: true args: { type: { @@ -86,43 +81,36 @@ allOptional: true } object: { type: Object.Object -typeName: "Object" nullable: true args: null } listListInt: { type: Array> -typeName: null nullable: false args: null } listInt: { type: Array<$.Scalars["Int"]> -typeName: null nullable: false args: null } listIntNonNull: { type: Array<$.Scalars["Int"]> -typeName: null nullable: false args: null } objectNested: { type: Object.ObjectNested -typeName: "ObjectNested" nullable: true args: null } objectNonNull: { type: Object.Object -typeName: "Object" nullable: false args: null } objectWithArgs: { type: Object.Object -typeName: "Object" nullable: true args: { type: { @@ -137,7 +125,6 @@ allOptional: true } fooBarUnion: { type: Union.FooBarUnion -typeName: "FooBarUnion" nullable: true args: null } @@ -146,7 +133,6 @@ args: null */ abcEnum: { type: Enum.ABCEnum -typeName: "ABCEnum" nullable: true args: null } @@ -211,7 +197,6 @@ args: null */ id: { type: $.Scalars["ID"] -typeName: "ID" nullable: true args: null } @@ -225,7 +210,6 @@ args: null } int: { type: $.Scalars["Int"] -typeName: "Int" nullable: true args: null } @@ -239,13 +223,11 @@ args: null } id: { type: $.Scalars["ID"] -typeName: "ID" nullable: true args: null } object: { type: Object.Object -typeName: "Object" nullable: true args: null } @@ -259,31 +241,26 @@ args: null } string: { type: $.Scalars["String"] -typeName: "String" nullable: true args: null } int: { type: $.Scalars["Int"] -typeName: "Int" nullable: true args: null } float: { type: $.Scalars["Float"] -typeName: "Float" nullable: true args: null } boolean: { type: $.Scalars["Boolean"] -typeName: "Boolean" nullable: true args: null } id: { type: $.Scalars["ID"] -typeName: "ID" nullable: true args: null } diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index 35f1192da..a43ad6673 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -45,25 +45,21 @@ args: null } id: { type: $.Scalars["ID"] -typeName: "ID" nullable: true args: null } idNonNull: { type: $.Scalars["ID"] -typeName: "ID" nullable: false args: null } string: { type: $.Scalars["String"] -typeName: "String" nullable: true args: null } stringWithRequiredArg: { type: $.Scalars["String"] -typeName: "String" nullable: true args: { type: { @@ -74,7 +70,6 @@ allOptional: false } stringWithArgs: { type: $.Scalars["String"] -typeName: "String" nullable: true args: { type: { @@ -89,43 +84,36 @@ allOptional: true } object: { type: Object.Object -typeName: "Object" nullable: true args: null } listListInt: { type: Array> -typeName: null nullable: false args: null } listInt: { type: Array<$.Scalars["Int"]> -typeName: null nullable: false args: null } listIntNonNull: { type: Array<$.Scalars["Int"]> -typeName: null nullable: false args: null } objectNested: { type: Object.ObjectNested -typeName: "ObjectNested" nullable: true args: null } objectNonNull: { type: Object.Object -typeName: "Object" nullable: false args: null } objectWithArgs: { type: Object.Object -typeName: "Object" nullable: true args: { type: { @@ -140,7 +128,6 @@ allOptional: true } fooBarUnion: { type: Union.FooBarUnion -typeName: "FooBarUnion" nullable: true args: null } @@ -149,7 +136,6 @@ args: null */ abcEnum: { type: Enum.ABCEnum -typeName: "ABCEnum" nullable: true args: null } @@ -214,7 +200,6 @@ args: null */ id: { type: $.Scalars["ID"] -typeName: "ID" nullable: true args: null } @@ -228,7 +213,6 @@ args: null } int: { type: $.Scalars["Int"] -typeName: "Int" nullable: true args: null } @@ -242,13 +226,11 @@ args: null } id: { type: $.Scalars["ID"] -typeName: "ID" nullable: true args: null } object: { type: Object.Object -typeName: "Object" nullable: true args: null } @@ -262,31 +244,26 @@ args: null } string: { type: $.Scalars["String"] -typeName: "String" nullable: true args: null } int: { type: $.Scalars["Int"] -typeName: "Int" nullable: true args: null } float: { type: $.Scalars["Float"] -typeName: "Float" nullable: true args: null } boolean: { type: $.Scalars["Boolean"] -typeName: "Boolean" nullable: true args: null } id: { type: $.Scalars["ID"] -typeName: "ID" nullable: true args: null } From 2539d1464d79212f2823746c319a880af0b63bb0 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 25 Feb 2024 15:05:38 -0500 Subject: [PATCH 36/71] wip --- src/ResultSet/ResultSet.test-d.ts | 3 +- src/ResultSet/ResultSet.ts | 17 +- src/SelectionSet/SelectionSet.test-d.ts | 9 +- src/SelectionSet/SelectionSet.ts | 78 ++- src/generator/generator.ts | 82 ++- src/lib/Code.ts | 12 +- src/schema/Schema.ts | 25 +- tests/builder/_/schema.graphql | 2 +- tests/builder/_/schema.ts | 600 +++++++++++------- .../__snapshots__/generate.test.ts.snap | 240 +++++-- 10 files changed, 694 insertions(+), 374 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index d977ee3e0..c8d27a24f 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -8,6 +8,7 @@ import type { ResultSet } from './__.js' type I = Schema.$.Index type RS<$S extends SelectionSet.Query> = ResultSet.Query<$S, I> +type x = RS<{ object: { id: true } }> // dprint-ignore test(`general`, () => { @@ -42,7 +43,7 @@ test(`general`, () => { // todo // List - // todo + expectTypeOf>().toEqualTypeOf<{ listInt: null|number[] }>() // Alias // scalar diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index abd8dbf6a..0935b1d90 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -58,12 +58,23 @@ type Union<$SelectionSet, $Node extends Schema.Union, $Index extends Schema.Inde type Field<$SelectionSet, $Field extends Schema.Field, $Index extends Schema.Index> = $SelectionSet extends SelectionSet.Directive.Include.Negative | SelectionSet.Directive.Skip.Positive ? null : ( - | FieldNullable<$Field> | FieldDirectiveInclude<$SelectionSet> | FieldDirectiveSkip<$SelectionSet> - | Node<$SelectionSet, $Field['type'], $Index> + | FieldUnwrap<$SelectionSet, $Field['type'], $Index> + // | Node<$SelectionSet, $Field['type'], $Index> ) +// dprint-ignore +type FieldUnwrap< +$SelectionSet, + T extends { kind: 'reference'; reference: any } | { kind: 'list'; type: any } | { kind: 'nullable'; type: any }, + $Index extends Schema.Index +> = + T extends { kind: 'nullable' } ? null | FieldUnwrap<$SelectionSet,T['type'],$Index> : + T extends { kind: 'list' } ? FieldUnwrap<$SelectionSet,T['type'],$Index>[] : + T extends { kind: 'reference' } ? Node<$SelectionSet, T['reference'], $Index> + : never + // dprint-ignore type FieldDirectiveInclude<$SelectionSet> = $SelectionSet extends SelectionSet.Directive.Include ? $SelectionSet extends SelectionSet.Directive.Include.Positive ? @@ -78,8 +89,6 @@ type FieldDirectiveSkip<$SelectionSet> = null : never -type FieldNullable<$Field extends Schema.Field> = $Field['nullable'] extends true ? null : never - // dprint-ignore export namespace Errors { export type UnknownNode<$Node extends Schema.Node> = diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 316cfe0a6..be2dbf06e 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -27,14 +27,15 @@ test(`Query`, () => { assertType({ abcEnum: true }) // Object + assertType({ object: { id: true } }) + // typename + assertType({ __typename: true }) + // Non-Null + assertType({ objectNonNull: { id: true } }) // @ts-expect-error excess property check assertType({ id2: true }) // @ts-expect-error excess property check assertType({ object: { a2: true } }) - assertType({ __typename: true }) - assertType({ object: { id: true } }) - // Non-Null - assertType({ objectNonNull: { id: true } }) // Union assertType({ fooBarUnion: { __typename: true } }) diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index b34e617b3..da5ef6b79 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -16,53 +16,68 @@ export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscrip ? SelectionSetObject<$Index['Root']['Subscription'], $Index> : never +// dprint-ignore type SelectionSetObject< $Object extends Schema.Object, $Index extends Schema.Index, > = - & { - [Key in keyof $Object]?: SelectionSetField< - Schema.AsField<$Object[Key]>, - $Index - > + & + { + [Key in keyof $Object]?: + SelectionSetField>, $Index> } - & /** + & + /** * Alias support. * Allow every field to also be given as a key with this pattern `_as_: ...` - */ { + */ + { [ Key in keyof $Object as `${keyof $Object & string}_as_${StringNonEmpty}` - ]?: SelectionSetField< - Schema.AsField<$Object[Key]>, - $Index - > + ]?: + SelectionSetField>, $Index> } & FieldDirectives - & /** + & + /** * Inline fragments for field groups. * @see https://spec.graphql.org/draft/#sec-Inline-Fragments - */ { + */ + { ___?: MaybeList> } - & /** + & + /** * Special property to select all scalars. - */ { $scalars?: ClientIndicator } + */ + { + $scalars?: ClientIndicator + } export type IsSelectScalarsWildcard = SS extends { $scalars: ClientIndicatorPositive } ? true : false // dprint-ignore export type SelectionSetField< - $Field extends Schema.Field, + $Field extends Schema.ScalarField|Schema.UnionField|Schema.ObjectField|Schema.LiteralField, $Index extends Schema.Index, -> = $Field extends Schema.ScalarField - ? Indicator<$Field> - : $Field extends Schema.unionField - ? SelectionSetUnion<$Field['type'], $Index> - : $Field extends Schema.ObjectField - ? SelectionSetObject<$Field['type'], $Index> & Arguments<$Field> - : TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> - -type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.Args +> = + $Field extends Schema.ScalarField ? Indicator<$Field> : + $Field extends Schema.LiteralField ? Indicator<$Field> : + $Field extends Schema.UnionField ? SelectionSetUnion<$Field['type']['reference'], $Index> : + $Field extends Schema.ObjectField ? SelectionSetObject<$Field['type']['reference'], $Index> & Arguments<$Field> : + TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> + +type UnwrapListAndNullableFieldType<$Field extends Schema.Field> = UnwrapListAndNullableFieldType_<$Field['type']> + +// dprint-ignore +type UnwrapListAndNullableFieldType_ = + FT extends Schema.FieldTypeList ? UnwrapListAndNullableFieldType_ : + FT extends Schema.FieldTypeNullable ? UnwrapListAndNullableFieldType_ : + FT extends Schema.FieldTypeLiteral ? FT : + FT extends Schema.FieldTypeReference ? FT + : TSError<'UnwrapFieldType_', 'FT case not handled', { FT: FT }> + +type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.FieldArgs ? $Field['args']['allOptional'] extends true ? { $?: $Field['args']['type'] } @@ -77,7 +92,7 @@ type SelectionSetUnion< $Index extends Schema.Index, > = & { - [Key in $Union['type']['__typename']['type'] as `on${Capitalize}`]?: SelectionSetObject< + [Key in $Union['type']['__typename']['type']['value'] as `on${Capitalize}`]?: SelectionSetObject< Extract<$Union['type'], { __typename: { type: Key } }>, $Index > @@ -167,12 +182,11 @@ export type OmitNegativeIndicators<$SelectionSet> = { export type NoArgsIndicator = ClientIndicator | FieldDirectives // dprint-ignore -export type Indicator<$FieldType extends Schema.Field> = - $FieldType['args'] extends Schema.Args - ? $FieldType['args']['allOptional'] extends true - ? ({ $?: $FieldType['args']['type'] } & FieldDirectives) | ClientIndicator - : { $: $FieldType['args']['type'] } & FieldDirectives - : NoArgsIndicator +export type Indicator<$FieldType extends Schema.LiteralField | Schema.ScalarField> = + $FieldType['args'] extends Schema.FieldArgs ? $FieldType['args']['allOptional'] extends true + ? ({ $?: $FieldType['args']['type'] } & FieldDirectives) | ClientIndicator + : { $: $FieldType['args']['type'] } & FieldDirectives + : NoArgsIndicator /** * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives diff --git a/src/generator/generator.ts b/src/generator/generator.ts index f29696254..071bdd239 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -1,4 +1,3 @@ -// todo Emit JSDoc from GraphQL descriptions import type { GraphQLArgument, GraphQLEnumValue, @@ -6,7 +5,7 @@ import type { GraphQLInterfaceType, GraphQLNamedType, } from 'graphql' -import { GraphQLNonNull, GraphQLObjectType, isEnumType, isNamedType } from 'graphql' +import { GraphQLNonNull, GraphQLObjectType, isEnumType, isListType, isNamedType } from 'graphql' import { buildSchema } from 'graphql' import _ from 'json-bigint' import fs from 'node:fs/promises' @@ -214,9 +213,11 @@ const renderFields = (config: Config, node: AnyGraphQLFieldsType): string => { Code.field( `__typename`, Code.objectFrom({ - type: { type: Code.quote(node.name) }, - nullable: { type: false }, - args: { type: null }, + type: Code.objectFrom({ + kind: Code.quote(`literal`), + value: Code.quote(node.name), + }), + args: null, }), ), ] @@ -232,20 +233,49 @@ const renderFields = (config: Config, node: AnyGraphQLFieldsType): string => { ])) } -const renderField = (config: Config, field: AnyField): string => { - const { node, nullable } = unwrapNonNull(field.type) +const buildType = (config: Config, node: AnyClass) => { + const { node: nodeInner, nullable } = unwrapNonNull(node) + + if (isNamedType(nodeInner)) { + const nodeCode = dispatchToReferenceRenderer(config, nodeInner) + const nodeType = Code.objectFrom({ + kind: Code.quote(`reference`), + reference: nodeCode, + }) + return nullable + ? Code.objectFrom({ + kind: Code.quote(`nullable`), + type: nodeType, + }) + : nodeType + } - const type = dispatchToReferenceRenderer(config, node) + if (isListType(nodeInner)) { + const nodeType = Code.objectFrom({ + kind: Code.quote(`list`), + type: buildType(config, nodeInner.ofType), + }) + return nullable + ? Code.objectFrom({ + kind: Code.quote(`nullable`), + type: nodeType, + }) + : nodeType + } + + throw new Error(`Unhandled type: ${String(node)}`) +} + +const renderField = (config: Config, field: AnyField): string => { + const type = buildType(config, field.type) const args = isGraphQLOutputField(field) && field.args.length > 0 ? renderArgs(config, field.args) : null - // const fieldWithArgs = args ? Code.intersection(fieldBase, args) : fieldBase return Code.objectFrom({ - type: { type }, - nullable: { type: nullable ? true : false }, - args: { type: args ?? null }, + type, + args, }) } @@ -339,23 +369,19 @@ export const generateCode = (input: Input) => { Code.objectFrom({ Root: { type: Code.objectFrom({ - Query: { type: hasQuery ? `Root.Query` : null }, - Mutation: { type: hasMutation ? `Root.Mutation` : null }, - Subscription: { type: hasSubscription ? `Root.Subscription` : null }, + Query: hasQuery ? `Root.Query` : null, + Mutation: hasMutation ? `Root.Mutation` : null, + Subscription: hasSubscription ? `Root.Subscription` : null, }), }, - objects: { - type: Code.objectFromEntries( - typeMapByKind.GraphQLObjectType.map(_ => [_.name, Code.propertyAccess(`Object`, _.name)]), - ), - }, - unionMemberNames: { - type: Code.objectFromEntries( - typeMapByKind.GraphQLUnionType.map( - (_) => [_.name, Code.unionItems(_.getTypes().map(_ => Code.quote(_.name)))], - ), + objects: Code.objectFromEntries( + typeMapByKind.GraphQLObjectType.map(_ => [_.name, Code.propertyAccess(`Object`, _.name)]), + ), + unionMemberNames: Code.objectFromEntries( + typeMapByKind.GraphQLUnionType.map( + (_) => [_.name, Code.unionItems(_.getTypes().map(_ => Code.quote(_.name)))], ), - }, + ), unions: { type: Code.objectFrom( { @@ -371,9 +397,7 @@ export const generateCode = (input: Input) => { }, ), }, - scalars: { - type: `Scalars`, - }, + scalars: `Scalars`, }), ), ), diff --git a/src/lib/Code.ts b/src/lib/Code.ts index 75a00bfe7..e9dd01dd3 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -16,11 +16,19 @@ export namespace Code { export const objectFromEntries = (entries: [string, string][]) => Code.objectFrom(Object.fromEntries(entries.map(([name, type]) => [name, { type }]))) export const objectFrom = ( - object: Record, + object: Record< + string, + null | string | boolean | number | { type: null | string | boolean | number; optional?: boolean; tsdoc?: string } + >, ) => Code.object( Code.fields( - Object.entries(object).map(([name, spec]) => Code.field(name, String(spec.type), { optional: spec.optional })), + Object.entries(object).map(([name, spec]) => + [name, spec && typeof spec === `object` ? spec : { type: spec }] as const + ) + .map(( + [name, spec], + ) => Code.field(name, String(spec.type), { optional: spec.optional })), ), ) export const interface$ = (name: string, object: string) => `interface ${name} ${object}` diff --git a/src/schema/Schema.ts b/src/schema/Schema.ts index 26b9e62a0..cc430983a 100644 --- a/src/schema/Schema.ts +++ b/src/schema/Schema.ts @@ -5,7 +5,6 @@ import type { Letter } from '../lib/prelude.js' export interface Index { unions: { Union: null | Union - // root: null | Object } Root: { Query: null | Object @@ -20,18 +19,26 @@ export interface Index { export type Nullable = null // todo needs to be extensible for custom scalars... export type Scalar = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] -export type Object = { __typename: { args: null; type: string } } +export type Object = { __typename: fieldTypename } export type Union = { __unionname: string; type: Object } export type Node = Object | Union | Scalar | Nullable -export type FieldBase<$Type extends Node> = { args: null | Args; type: $Type; nullable: boolean } -export type NullableField = FieldBase -export type ScalarField = FieldBase -export type ObjectField = FieldBase -export type unionField = FieldBase -export type Field = FieldBase +export type fieldTypename = { args: null; type: FieldTypeLiteral } +export type FieldTypeReference = { kind: 'reference'; reference: any } +export type FieldTypeLiteral = { kind: 'literal'; value: string } +export type FieldTypeList = { kind: 'list'; type: any } +export type FieldTypeNullable = { kind: 'nullable'; type: any } +export type FieldType = FieldTypeReference | FieldTypeList | FieldTypeNullable | FieldTypeLiteral +export type Field<$FieldType extends FieldType = FieldType> = { + args: null | FieldArgs + type: $FieldType +} +export type ScalarField = Field +export type ObjectField = Field +export type LiteralField = Field +export type UnionField = Field -export type Args = { type: object; allOptional: boolean } +export type FieldArgs = { type: object; allOptional: boolean } export type AsField = T extends Field ? T : never diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index 1b898e5b2..a7b600491 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -6,7 +6,7 @@ type Query { stringWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): String object: Object listListInt: [[Int!]!]! - listInt: [Int!]! + listInt: [Int] listIntNonNull: [Int!]! objectNested: ObjectNested objectNonNull: Object! diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 6416d998d..a258b9b67 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -1,32 +1,33 @@ export namespace $ { -export interface Index { -Root: { -Query: Root.Query -Mutation: null -Subscription: null -} -objects: { -Foo: Object.Foo -Bar: Object.Bar -ObjectNested: Object.ObjectNested -Object: Object.Object -} -unionMemberNames: { -FooBarUnion: "Foo" -| "Bar" -} -unions: { -Union: Union.FooBarUnion -} -scalars: Scalars -} -export interface Scalars { -ID: string -String: string -Int: number -Float: number -Boolean: boolean -} + export interface Index { + Root: { + Query: Root.Query + Mutation: null + Subscription: null + } + objects: { + Foo: Object.Foo + Bar: Object.Bar + ObjectNested: Object.ObjectNested + Object: Object.Object + } + unionMemberNames: { + FooBarUnion: + | 'Foo' + | 'Bar' + } + unions: { + Union: Union.FooBarUnion + } + scalars: Scalars + } + export interface Scalars { + ID: string + String: string + Int: number + Float: number + Boolean: boolean + } } // ------------------------------------------------------------ // @@ -34,109 +35,184 @@ Boolean: boolean // ------------------------------------------------------------ // export namespace Root { -export interface Query { -__typename: { -type: "Query" -nullable: false -args: null -} -id: { -type: $.Scalars["ID"] -nullable: true -args: null -} -idNonNull: { -type: $.Scalars["ID"] -nullable: false -args: null -} -string: { -type: $.Scalars["String"] -nullable: true -args: null -} -stringWithRequiredArg: { -type: $.Scalars["String"] -nullable: true -args: { -type: { -string: $.Scalars["String"] -} -allOptional: false -} -} -stringWithArgs: { -type: $.Scalars["String"] -nullable: true -args: { -type: { -string?: $.Scalars["String"] | null -int?: $.Scalars["Int"] | null -float?: $.Scalars["Float"] | null -boolean?: $.Scalars["Boolean"] | null -id?: $.Scalars["ID"] | null -} -allOptional: true -} -} -object: { -type: Object.Object -nullable: true -args: null -} -listListInt: { -type: Array> -nullable: false -args: null -} -listInt: { -type: Array<$.Scalars["Int"]> -nullable: false -args: null -} -listIntNonNull: { -type: Array<$.Scalars["Int"]> -nullable: false -args: null -} -objectNested: { -type: Object.ObjectNested -nullable: true -args: null -} -objectNonNull: { -type: Object.Object -nullable: false -args: null -} -objectWithArgs: { -type: Object.Object -nullable: true -args: { -type: { -string?: $.Scalars["String"] | null -int?: $.Scalars["Int"] | null -float?: $.Scalars["Float"] | null -boolean?: $.Scalars["Boolean"] | null -id?: $.Scalars["ID"] | null -} -allOptional: true -} -} -fooBarUnion: { -type: Union.FooBarUnion -nullable: true -args: null -} -/** -* Query enum field documentation. -*/ -abcEnum: { -type: Enum.ABCEnum -nullable: true -args: null -} -} + export interface Query { + __typename: { + type: { + kind: 'literal' + value: 'Query' + } + args: null + } + id: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['ID'] + } + } + args: null + } + idNonNull: { + type: { + kind: 'reference' + reference: $.Scalars['ID'] + } + args: null + } + string: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['String'] + } + } + args: null + } + stringWithRequiredArg: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['String'] + } + } + args: { + type: { + string: $.Scalars['String'] + } + allOptional: false + } + } + stringWithArgs: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['String'] + } + } + args: { + type: { + string?: $.Scalars['String'] | null + int?: $.Scalars['Int'] | null + float?: $.Scalars['Float'] | null + boolean?: $.Scalars['Boolean'] | null + id?: $.Scalars['ID'] | null + } + allOptional: true + } + } + object: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: Object.Object + } + } + args: null + } + listListInt: { + type: { + kind: 'list' + type: { + kind: 'list' + type: { + kind: 'reference' + reference: $.Scalars['Int'] + } + } + } + args: null + } + listInt: { + type: { + kind: 'nullable' + type: { + kind: 'list' + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['Int'] + } + } + } + } + args: null + } + listIntNonNull: { + type: { + kind: 'list' + type: { + kind: 'reference' + reference: $.Scalars['Int'] + } + } + args: null + } + objectNested: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: Object.ObjectNested + } + } + args: null + } + objectNonNull: { + type: { + kind: 'reference' + reference: Object.Object + } + args: null + } + objectWithArgs: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: Object.Object + } + } + args: { + type: { + string?: $.Scalars['String'] | null + int?: $.Scalars['Int'] | null + float?: $.Scalars['Float'] | null + boolean?: $.Scalars['Boolean'] | null + id?: $.Scalars['ID'] | null + } + allOptional: true + } + } + fooBarUnion: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: Union.FooBarUnion + } + } + args: null + } + /** + * Query enum field documentation. + */ + abcEnum: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: Enum.ABCEnum + } + } + args: null + } + } } // ------------------------------------------------------------ // @@ -144,18 +220,18 @@ args: null // ------------------------------------------------------------ // export namespace Enum { -/** -* Enum documentation. -* -* Members -* "A" - (DEPRECATED: Enum value A is deprecated.) -* "B" - Enum B member documentation. -* "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) -*/ -export type ABCEnum = -| "A" -| "B" -| "C" + /** + * Enum documentation. + * + * Members + * "A" - (DEPRECATED: Enum value A is deprecated.) + * "B" - Enum B member documentation. + * "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) + */ + export type ABCEnum = + | 'A' + | 'B' + | 'C' } // ------------------------------------------------------------ // @@ -163,8 +239,7 @@ export type ABCEnum = // ------------------------------------------------------------ // export namespace InputObject { -// -- no types -- - + // -- no types -- } // ------------------------------------------------------------ // @@ -172,8 +247,7 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { -// -- no types -- - + // -- no types -- } // ------------------------------------------------------------ // @@ -181,90 +255,143 @@ export namespace Interface { // ------------------------------------------------------------ // export namespace Object { -/** -* Object documentation. -*/ -export interface Foo { -__typename: { -type: "Foo" -nullable: false -args: null -} -/** -* Field documentation. -* -* @deprecated Field a is deprecated. -*/ -id: { -type: $.Scalars["ID"] -nullable: true -args: null -} -} + /** + * Object documentation. + */ + export interface Foo { + __typename: { + type: { + kind: 'literal' + value: 'Foo' + } + args: null + } + /** + * Field documentation. + * + * @deprecated Field a is deprecated. + */ + id: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['ID'] + } + } + args: null + } + } -export interface Bar { -__typename: { -type: "Bar" -nullable: false -args: null -} -int: { -type: $.Scalars["Int"] -nullable: true -args: null -} -} + export interface Bar { + __typename: { + type: { + kind: 'literal' + value: 'Bar' + } + args: null + } + int: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['Int'] + } + } + args: null + } + } -export interface ObjectNested { -__typename: { -type: "ObjectNested" -nullable: false -args: null -} -id: { -type: $.Scalars["ID"] -nullable: true -args: null -} -object: { -type: Object.Object -nullable: true -args: null -} -} + export interface ObjectNested { + __typename: { + type: { + kind: 'literal' + value: 'ObjectNested' + } + args: null + } + id: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['ID'] + } + } + args: null + } + object: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: Object.Object + } + } + args: null + } + } -export interface Object { -__typename: { -type: "Object" -nullable: false -args: null -} -string: { -type: $.Scalars["String"] -nullable: true -args: null -} -int: { -type: $.Scalars["Int"] -nullable: true -args: null -} -float: { -type: $.Scalars["Float"] -nullable: true -args: null -} -boolean: { -type: $.Scalars["Boolean"] -nullable: true -args: null -} -id: { -type: $.Scalars["ID"] -nullable: true -args: null -} -} + export interface Object { + __typename: { + type: { + kind: 'literal' + value: 'Object' + } + args: null + } + string: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['String'] + } + } + args: null + } + int: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['Int'] + } + } + args: null + } + float: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['Float'] + } + } + args: null + } + boolean: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['Boolean'] + } + } + args: null + } + id: { + type: { + kind: 'nullable' + type: { + kind: 'reference' + reference: $.Scalars['ID'] + } + } + args: null + } + } } // ------------------------------------------------------------ // @@ -272,12 +399,13 @@ args: null // ------------------------------------------------------------ // export namespace Union { -/** -* Union documentation. -*/ -export interface FooBarUnion { -__unionname: "FooBarUnion" -type: Object.Foo -| Object.Bar + /** + * Union documentation. + */ + export interface FooBarUnion { + __unionname: 'FooBarUnion' + type: + | Object.Foo + | Object.Bar + } } -} \ No newline at end of file diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index a43ad6673..84c964a4d 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -39,28 +39,47 @@ Boolean: boolean export namespace Root { export interface Query { __typename: { -type: "Query" -nullable: false +type: { +kind: "literal" +value: "Query" +} args: null } id: { -type: $.Scalars["ID"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["ID"] +} +} args: null } idNonNull: { -type: $.Scalars["ID"] -nullable: false +type: { +kind: "reference" +reference: $.Scalars["ID"] +} args: null } string: { -type: $.Scalars["String"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["String"] +} +} args: null } stringWithRequiredArg: { -type: $.Scalars["String"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["String"] +} +} args: { type: { string: $.Scalars["String"] @@ -69,8 +88,13 @@ allOptional: false } } stringWithArgs: { -type: $.Scalars["String"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["String"] +} +} args: { type: { string?: $.Scalars["String"] | null @@ -83,38 +107,79 @@ allOptional: true } } object: { -type: Object.Object -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: Object.Object +} +} args: null } listListInt: { -type: Array> -nullable: false +type: { +kind: "list" +type: { +kind: "list" +type: { +kind: "reference" +reference: $.Scalars["Int"] +} +} +} args: null } listInt: { -type: Array<$.Scalars["Int"]> -nullable: false +type: { +kind: "nullable" +type: { +kind: "list" +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["Int"] +} +} +} +} args: null } listIntNonNull: { -type: Array<$.Scalars["Int"]> -nullable: false +type: { +kind: "list" +type: { +kind: "reference" +reference: $.Scalars["Int"] +} +} args: null } objectNested: { -type: Object.ObjectNested -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: Object.ObjectNested +} +} args: null } objectNonNull: { -type: Object.Object -nullable: false +type: { +kind: "reference" +reference: Object.Object +} args: null } objectWithArgs: { -type: Object.Object -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: Object.Object +} +} args: { type: { string?: $.Scalars["String"] | null @@ -127,16 +192,26 @@ allOptional: true } } fooBarUnion: { -type: Union.FooBarUnion -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: Union.FooBarUnion +} +} args: null } /** * Query enum field documentation. */ abcEnum: { -type: Enum.ABCEnum -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: Enum.ABCEnum +} +} args: null } } @@ -189,8 +264,10 @@ export namespace Object { */ export interface Foo { __typename: { -type: "Foo" -nullable: false +type: { +kind: "literal" +value: "Foo" +} args: null } /** @@ -199,72 +276,123 @@ args: null * @deprecated Field a is deprecated. */ id: { -type: $.Scalars["ID"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["ID"] +} +} args: null } } export interface Bar { __typename: { -type: "Bar" -nullable: false +type: { +kind: "literal" +value: "Bar" +} args: null } int: { -type: $.Scalars["Int"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["Int"] +} +} args: null } } export interface ObjectNested { __typename: { -type: "ObjectNested" -nullable: false +type: { +kind: "literal" +value: "ObjectNested" +} args: null } id: { -type: $.Scalars["ID"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["ID"] +} +} args: null } object: { -type: Object.Object -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: Object.Object +} +} args: null } } export interface Object { __typename: { -type: "Object" -nullable: false +type: { +kind: "literal" +value: "Object" +} args: null } string: { -type: $.Scalars["String"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["String"] +} +} args: null } int: { -type: $.Scalars["Int"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["Int"] +} +} args: null } float: { -type: $.Scalars["Float"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["Float"] +} +} args: null } boolean: { -type: $.Scalars["Boolean"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["Boolean"] +} +} args: null } id: { -type: $.Scalars["ID"] -nullable: true +type: { +kind: "nullable" +type: { +kind: "reference" +reference: $.Scalars["ID"] +} +} args: null } } From 5e7322b2857cf16ce2f834f01cbfcaaed4dd89f1 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 25 Feb 2024 22:04:05 -0500 Subject: [PATCH 37/71] work --- src/ResultSet/ResultSet.ts | 2 +- src/SelectionSet/SelectionSet.ts | 73 +- src/generator/generator.ts | 24 +- src/schema/Schema.ts | 35 +- tests/builder/_/schema.ts | 756 +++++++++--------- .../__snapshots__/generate.test.ts.snap | 120 +-- 6 files changed, 540 insertions(+), 470 deletions(-) diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index 0935b1d90..9d3ef8da7 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -32,7 +32,7 @@ export type Object<$SelectionSet, $Object extends Schema.Object, $Index extends */ ? { - [$Key in keyof $Object as $Object[$Key] extends Schema.ScalarField ? $Key : never]: + [$Key in keyof $Object as $Object[$Key] extends Schema.FieldScalar ? $Key : never]: Field<$SelectionSet, Schema.AsField<$Object[$Key]>, $Index> } : ( diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index da5ef6b79..bbcbcfd1f 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -4,27 +4,27 @@ import type { MaybeList, StringNonEmpty, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../schema/__.js' -export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Object - ? SelectionSetObject<$Index['Root']['Query'], $Index> +export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Object + ? Object<$Index['Root']['Query'], $Index> : never -export type Mutation<$Index extends Schema.Index> = $Index['Root']['Mutation'] extends Object - ? SelectionSetObject<$Index['Root']['Mutation'], $Index> +export type Mutation<$Index extends Schema.Index> = $Index['Root']['Mutation'] extends Schema.Object + ? Object<$Index['Root']['Mutation'], $Index> : never -export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscription'] extends Object - ? SelectionSetObject<$Index['Root']['Subscription'], $Index> +export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscription'] extends Schema.Object + ? Object<$Index['Root']['Subscription'], $Index> : never // dprint-ignore -type SelectionSetObject< +type Object< $Object extends Schema.Object, $Index extends Schema.Index, > = & { [Key in keyof $Object]?: - SelectionSetField>, $Index> + Field, $Index> } & /** @@ -35,7 +35,7 @@ type SelectionSetObject< [ Key in keyof $Object as `${keyof $Object & string}_as_${StringNonEmpty}` ]?: - SelectionSetField>, $Index> + Field, $Index> } & FieldDirectives & @@ -44,7 +44,7 @@ type SelectionSetObject< * @see https://spec.graphql.org/draft/#sec-Inline-Fragments */ { - ___?: MaybeList> + ___?: MaybeList> } & /** @@ -57,25 +57,25 @@ type SelectionSetObject< export type IsSelectScalarsWildcard = SS extends { $scalars: ClientIndicatorPositive } ? true : false // dprint-ignore -export type SelectionSetField< - $Field extends Schema.ScalarField|Schema.UnionField|Schema.ObjectField|Schema.LiteralField, +export type Field< + $Field extends Schema.Field, $Index extends Schema.Index, > = - $Field extends Schema.ScalarField ? Indicator<$Field> : - $Field extends Schema.LiteralField ? Indicator<$Field> : - $Field extends Schema.UnionField ? SelectionSetUnion<$Field['type']['reference'], $Index> : - $Field extends Schema.ObjectField ? SelectionSetObject<$Field['type']['reference'], $Index> & Arguments<$Field> : + $Field extends Schema.FieldTypename ? NoArgsIndicator : + $Field extends Schema.FieldScalar ? Indicator<$Field> : + $Field extends Schema.FieldUnion ? Union<$Field['namedType'], $Index> : + $Field extends Schema.FieldObject ? Object<$Field['namedType'], $Index> & Arguments<$Field> : TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> -type UnwrapListAndNullableFieldType<$Field extends Schema.Field> = UnwrapListAndNullableFieldType_<$Field['type']> +// type UnwrapListAndNullableFieldType<$Field extends Schema.Field> = UnwrapListAndNullableFieldType_<$Field['type']> -// dprint-ignore -type UnwrapListAndNullableFieldType_ = - FT extends Schema.FieldTypeList ? UnwrapListAndNullableFieldType_ : - FT extends Schema.FieldTypeNullable ? UnwrapListAndNullableFieldType_ : - FT extends Schema.FieldTypeLiteral ? FT : - FT extends Schema.FieldTypeReference ? FT - : TSError<'UnwrapFieldType_', 'FT case not handled', { FT: FT }> +// // dprint-ignore +// type UnwrapListAndNullableFieldType_ = +// FT extends Schema.FieldTypeList ? UnwrapListAndNullableFieldType_ : +// FT extends Schema.FieldTypeNullable ? UnwrapListAndNullableFieldType_ : +// FT extends Schema.FieldTypeLiteral ? FT : +// FT extends Schema.FieldTypeNamed ? FT +// : TSError<'UnwrapFieldType_', 'FT case not handled', { FT: FT }> type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.FieldArgs ? $Field['args']['allOptional'] extends true ? { @@ -87,17 +87,18 @@ type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.Fiel : {} // TODO why does $object not get passed to this in a distributed way? -type SelectionSetUnion< +// dprint-ignore +type Union< $Union extends Schema.Union, $Index extends Schema.Index, > = & { - [Key in $Union['type']['__typename']['type']['value'] as `on${Capitalize}`]?: SelectionSetObject< - Extract<$Union['type'], { __typename: { type: Key } }>, - $Index - > - } - & { __typename?: NoArgsIndicator } + [Key in $Union['type']['__typename']['namedType'] as `on${Capitalize}`]?: + Object, $Index> + } + & { + __typename?: NoArgsIndicator + } /** * Helpers @@ -182,11 +183,11 @@ export type OmitNegativeIndicators<$SelectionSet> = { export type NoArgsIndicator = ClientIndicator | FieldDirectives // dprint-ignore -export type Indicator<$FieldType extends Schema.LiteralField | Schema.ScalarField> = - $FieldType['args'] extends Schema.FieldArgs ? $FieldType['args']['allOptional'] extends true - ? ({ $?: $FieldType['args']['type'] } & FieldDirectives) | ClientIndicator - : { $: $FieldType['args']['type'] } & FieldDirectives - : NoArgsIndicator +export type Indicator<$Field extends Schema.FieldScalar> = + $Field['args'] extends Schema.FieldArgs ? $Field['args']['allOptional'] extends true + ? ({ $?: $Field['args']['type'] } & FieldDirectives) | ClientIndicator + : { $: $Field['args']['type'] } & FieldDirectives + : NoArgsIndicator /** * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 071bdd239..5a9256057 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -5,7 +5,7 @@ import type { GraphQLInterfaceType, GraphQLNamedType, } from 'graphql' -import { GraphQLNonNull, GraphQLObjectType, isEnumType, isListType, isNamedType } from 'graphql' +import { GraphQLNonNull, GraphQLObjectType, isEnumType, isListType, isNamedType, isNonNullType } from 'graphql' import { buildSchema } from 'graphql' import _ from 'json-bigint' import fs from 'node:fs/promises' @@ -218,6 +218,7 @@ const renderFields = (config: Config, node: AnyGraphQLFieldsType): string => { value: Code.quote(node.name), }), args: null, + namedType: Code.quote(node.name), }), ), ] @@ -237,17 +238,17 @@ const buildType = (config: Config, node: AnyClass) => { const { node: nodeInner, nullable } = unwrapNonNull(node) if (isNamedType(nodeInner)) { - const nodeCode = dispatchToReferenceRenderer(config, nodeInner) - const nodeType = Code.objectFrom({ - kind: Code.quote(`reference`), - reference: nodeCode, + const namedType = dispatchToReferenceRenderer(config, nodeInner) + const type = Code.objectFrom({ + kind: Code.quote(`named`), + named: namedType, }) return nullable ? Code.objectFrom({ kind: Code.quote(`nullable`), - type: nodeType, + type: type, }) - : nodeType + : type } if (isListType(nodeInner)) { @@ -266,8 +267,16 @@ const buildType = (config: Config, node: AnyClass) => { throw new Error(`Unhandled type: ${String(node)}`) } +const getNamedType = (config: Config, node: AnyClass): GraphQLNamedType => { + if (isNamedType(node)) return node + if (isNonNullType(node)) return getNamedType(config, node.ofType) + if (isListType(node)) return getNamedType(config, node.ofType) + throw new Error(`Unhandled type: ${String(node)}`) +} + const renderField = (config: Config, field: AnyField): string => { const type = buildType(config, field.type) + const namedType = dispatchToReferenceRenderer(config, getNamedType(config, field.type)) const args = isGraphQLOutputField(field) && field.args.length > 0 ? renderArgs(config, field.args) @@ -275,6 +284,7 @@ const renderField = (config: Config, field: AnyField): string => { return Code.objectFrom({ type, + namedType, args, }) } diff --git a/src/schema/Schema.ts b/src/schema/Schema.ts index cc430983a..5862f150f 100644 --- a/src/schema/Schema.ts +++ b/src/schema/Schema.ts @@ -3,40 +3,43 @@ import type { Letter } from '../lib/prelude.js' export interface Index { - unions: { - Union: null | Union - } Root: { Query: null | Object Mutation: null | Object Subscription: null | Object } - scalars: object objects: Record + unions: { + Union: null | Union + } + scalars: object unionMemberNames: Record } -export type Nullable = null +// export type Nullable = null // todo needs to be extensible for custom scalars... export type Scalar = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] -export type Object = { __typename: fieldTypename } +export type Object = { __typename: FieldTypename } export type Union = { __unionname: string; type: Object } -export type Node = Object | Union | Scalar | Nullable +export type Literal = string +export type Named = Scalar | Object | Union | Literal +export type Node = Object | Union | Scalar // | Nullable -export type fieldTypename = { args: null; type: FieldTypeLiteral } -export type FieldTypeReference = { kind: 'reference'; reference: any } +export type FieldTypeNamed = { kind: 'named'; named: any } export type FieldTypeLiteral = { kind: 'literal'; value: string } export type FieldTypeList = { kind: 'list'; type: any } export type FieldTypeNullable = { kind: 'nullable'; type: any } -export type FieldType = FieldTypeReference | FieldTypeList | FieldTypeNullable | FieldTypeLiteral -export type Field<$FieldType extends FieldType = FieldType> = { +export type FieldType = FieldTypeNamed | FieldTypeList | FieldTypeNullable | FieldTypeLiteral + +export type Field<$Named extends Named = Named> = { args: null | FieldArgs - type: $FieldType + type: FieldType + namedType: $Named } -export type ScalarField = Field -export type ObjectField = Field -export type LiteralField = Field -export type UnionField = Field +export type FieldScalar = Field +export type FieldObject = Field +export type FieldUnion = Field +export type FieldTypename = { args: null; type: FieldTypeLiteral; namedType: string } export type FieldArgs = { type: object; allOptional: boolean } diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index a258b9b67..df978d8a4 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -1,33 +1,32 @@ export namespace $ { - export interface Index { - Root: { - Query: Root.Query - Mutation: null - Subscription: null - } - objects: { - Foo: Object.Foo - Bar: Object.Bar - ObjectNested: Object.ObjectNested - Object: Object.Object - } - unionMemberNames: { - FooBarUnion: - | 'Foo' - | 'Bar' - } - unions: { - Union: Union.FooBarUnion - } - scalars: Scalars - } - export interface Scalars { - ID: string - String: string - Int: number - Float: number - Boolean: boolean - } +export interface Index { +Root: { +Query: Root.Query +Mutation: null +Subscription: null +} +objects: { +Foo: Object.Foo +Bar: Object.Bar +ObjectNested: Object.ObjectNested +Object: Object.Object +} +unionMemberNames: { +FooBarUnion: "Foo" +| "Bar" +} +unions: { +Union: Union.FooBarUnion +} +scalars: Scalars +} +export interface Scalars { +ID: string +String: string +Int: number +Float: number +Boolean: boolean +} } // ------------------------------------------------------------ // @@ -35,184 +34,199 @@ export namespace $ { // ------------------------------------------------------------ // export namespace Root { - export interface Query { - __typename: { - type: { - kind: 'literal' - value: 'Query' - } - args: null - } - id: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['ID'] - } - } - args: null - } - idNonNull: { - type: { - kind: 'reference' - reference: $.Scalars['ID'] - } - args: null - } - string: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['String'] - } - } - args: null - } - stringWithRequiredArg: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['String'] - } - } - args: { - type: { - string: $.Scalars['String'] - } - allOptional: false - } - } - stringWithArgs: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['String'] - } - } - args: { - type: { - string?: $.Scalars['String'] | null - int?: $.Scalars['Int'] | null - float?: $.Scalars['Float'] | null - boolean?: $.Scalars['Boolean'] | null - id?: $.Scalars['ID'] | null - } - allOptional: true - } - } - object: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: Object.Object - } - } - args: null - } - listListInt: { - type: { - kind: 'list' - type: { - kind: 'list' - type: { - kind: 'reference' - reference: $.Scalars['Int'] - } - } - } - args: null - } - listInt: { - type: { - kind: 'nullable' - type: { - kind: 'list' - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['Int'] - } - } - } - } - args: null - } - listIntNonNull: { - type: { - kind: 'list' - type: { - kind: 'reference' - reference: $.Scalars['Int'] - } - } - args: null - } - objectNested: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: Object.ObjectNested - } - } - args: null - } - objectNonNull: { - type: { - kind: 'reference' - reference: Object.Object - } - args: null - } - objectWithArgs: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: Object.Object - } - } - args: { - type: { - string?: $.Scalars['String'] | null - int?: $.Scalars['Int'] | null - float?: $.Scalars['Float'] | null - boolean?: $.Scalars['Boolean'] | null - id?: $.Scalars['ID'] | null - } - allOptional: true - } - } - fooBarUnion: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: Union.FooBarUnion - } - } - args: null - } - /** - * Query enum field documentation. - */ - abcEnum: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: Enum.ABCEnum - } - } - args: null - } - } +export interface Query { +__typename: { +type: { +kind: "literal" +value: "Query" +} +args: null +namedType: "Query" +} +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +idNonNull: { +type: { +kind: "named" +named: $.Scalars["ID"] +} +namedType: $.Scalars["ID"] +args: null +} +string: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["String"] +} +} +namedType: $.Scalars["String"] +args: null +} +stringWithRequiredArg: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["String"] +} +} +namedType: $.Scalars["String"] +args: { +type: { +string: $.Scalars["String"] +} +allOptional: false +} +} +stringWithArgs: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["String"] +} +} +namedType: $.Scalars["String"] +args: { +type: { +string?: $.Scalars["String"] | null +int?: $.Scalars["Int"] | null +float?: $.Scalars["Float"] | null +boolean?: $.Scalars["Boolean"] | null +id?: $.Scalars["ID"] | null +} +allOptional: true +} +} +object: { +type: { +kind: "nullable" +type: { +kind: "named" +named: Object.Object +} +} +namedType: Object.Object +args: null +} +listListInt: { +type: { +kind: "list" +type: { +kind: "list" +type: { +kind: "named" +named: $.Scalars["Int"] +} +} +} +namedType: $.Scalars["Int"] +args: null +} +listInt: { +type: { +kind: "nullable" +type: { +kind: "list" +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["Int"] +} +} +} +} +namedType: $.Scalars["Int"] +args: null +} +listIntNonNull: { +type: { +kind: "list" +type: { +kind: "named" +named: $.Scalars["Int"] +} +} +namedType: $.Scalars["Int"] +args: null +} +objectNested: { +type: { +kind: "nullable" +type: { +kind: "named" +named: Object.ObjectNested +} +} +namedType: Object.ObjectNested +args: null +} +objectNonNull: { +type: { +kind: "named" +named: Object.Object +} +namedType: Object.Object +args: null +} +objectWithArgs: { +type: { +kind: "nullable" +type: { +kind: "named" +named: Object.Object +} +} +namedType: Object.Object +args: { +type: { +string?: $.Scalars["String"] | null +int?: $.Scalars["Int"] | null +float?: $.Scalars["Float"] | null +boolean?: $.Scalars["Boolean"] | null +id?: $.Scalars["ID"] | null +} +allOptional: true +} +} +fooBarUnion: { +type: { +kind: "nullable" +type: { +kind: "named" +named: Union.FooBarUnion +} +} +namedType: Union.FooBarUnion +args: null +} +/** +* Query enum field documentation. +*/ +abcEnum: { +type: { +kind: "nullable" +type: { +kind: "named" +named: Enum.ABCEnum +} +} +namedType: Enum.ABCEnum +args: null +} +} } // ------------------------------------------------------------ // @@ -220,18 +234,18 @@ export namespace Root { // ------------------------------------------------------------ // export namespace Enum { - /** - * Enum documentation. - * - * Members - * "A" - (DEPRECATED: Enum value A is deprecated.) - * "B" - Enum B member documentation. - * "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) - */ - export type ABCEnum = - | 'A' - | 'B' - | 'C' +/** +* Enum documentation. +* +* Members +* "A" - (DEPRECATED: Enum value A is deprecated.) +* "B" - Enum B member documentation. +* "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) +*/ +export type ABCEnum = +| "A" +| "B" +| "C" } // ------------------------------------------------------------ // @@ -239,7 +253,8 @@ export namespace Enum { // ------------------------------------------------------------ // export namespace InputObject { - // -- no types -- +// -- no types -- + } // ------------------------------------------------------------ // @@ -247,7 +262,8 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { - // -- no types -- +// -- no types -- + } // ------------------------------------------------------------ // @@ -255,143 +271,156 @@ export namespace Interface { // ------------------------------------------------------------ // export namespace Object { - /** - * Object documentation. - */ - export interface Foo { - __typename: { - type: { - kind: 'literal' - value: 'Foo' - } - args: null - } - /** - * Field documentation. - * - * @deprecated Field a is deprecated. - */ - id: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['ID'] - } - } - args: null - } - } +/** +* Object documentation. +*/ +export interface Foo { +__typename: { +type: { +kind: "literal" +value: "Foo" +} +args: null +namedType: "Foo" +} +/** +* Field documentation. +* +* @deprecated Field a is deprecated. +*/ +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +} - export interface Bar { - __typename: { - type: { - kind: 'literal' - value: 'Bar' - } - args: null - } - int: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['Int'] - } - } - args: null - } - } +export interface Bar { +__typename: { +type: { +kind: "literal" +value: "Bar" +} +args: null +namedType: "Bar" +} +int: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["Int"] +} +} +namedType: $.Scalars["Int"] +args: null +} +} - export interface ObjectNested { - __typename: { - type: { - kind: 'literal' - value: 'ObjectNested' - } - args: null - } - id: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['ID'] - } - } - args: null - } - object: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: Object.Object - } - } - args: null - } - } +export interface ObjectNested { +__typename: { +type: { +kind: "literal" +value: "ObjectNested" +} +args: null +namedType: "ObjectNested" +} +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +object: { +type: { +kind: "nullable" +type: { +kind: "named" +named: Object.Object +} +} +namedType: Object.Object +args: null +} +} - export interface Object { - __typename: { - type: { - kind: 'literal' - value: 'Object' - } - args: null - } - string: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['String'] - } - } - args: null - } - int: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['Int'] - } - } - args: null - } - float: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['Float'] - } - } - args: null - } - boolean: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['Boolean'] - } - } - args: null - } - id: { - type: { - kind: 'nullable' - type: { - kind: 'reference' - reference: $.Scalars['ID'] - } - } - args: null - } - } +export interface Object { +__typename: { +type: { +kind: "literal" +value: "Object" +} +args: null +namedType: "Object" +} +string: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["String"] +} +} +namedType: $.Scalars["String"] +args: null +} +int: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["Int"] +} +} +namedType: $.Scalars["Int"] +args: null +} +float: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["Float"] +} +} +namedType: $.Scalars["Float"] +args: null +} +boolean: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["Boolean"] +} +} +namedType: $.Scalars["Boolean"] +args: null +} +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +} } // ------------------------------------------------------------ // @@ -399,13 +428,12 @@ export namespace Object { // ------------------------------------------------------------ // export namespace Union { - /** - * Union documentation. - */ - export interface FooBarUnion { - __unionname: 'FooBarUnion' - type: - | Object.Foo - | Object.Bar - } +/** +* Union documentation. +*/ +export interface FooBarUnion { +__unionname: "FooBarUnion" +type: Object.Foo +| Object.Bar } +} \ No newline at end of file diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index 84c964a4d..a7308693e 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -44,42 +44,47 @@ kind: "literal" value: "Query" } args: null +namedType: "Query" } id: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["ID"] +kind: "named" +named: $.Scalars["ID"] } } +namedType: $.Scalars["ID"] args: null } idNonNull: { type: { -kind: "reference" -reference: $.Scalars["ID"] +kind: "named" +named: $.Scalars["ID"] } +namedType: $.Scalars["ID"] args: null } string: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["String"] +kind: "named" +named: $.Scalars["String"] } } +namedType: $.Scalars["String"] args: null } stringWithRequiredArg: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["String"] +kind: "named" +named: $.Scalars["String"] } } +namedType: $.Scalars["String"] args: { type: { string: $.Scalars["String"] @@ -91,10 +96,11 @@ stringWithArgs: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["String"] +kind: "named" +named: $.Scalars["String"] } } +namedType: $.Scalars["String"] args: { type: { string?: $.Scalars["String"] | null @@ -110,10 +116,11 @@ object: { type: { kind: "nullable" type: { -kind: "reference" -reference: Object.Object +kind: "named" +named: Object.Object } } +namedType: Object.Object args: null } listListInt: { @@ -122,11 +129,12 @@ kind: "list" type: { kind: "list" type: { -kind: "reference" -reference: $.Scalars["Int"] +kind: "named" +named: $.Scalars["Int"] } } } +namedType: $.Scalars["Int"] args: null } listInt: { @@ -137,49 +145,54 @@ kind: "list" type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["Int"] +kind: "named" +named: $.Scalars["Int"] } } } } +namedType: $.Scalars["Int"] args: null } listIntNonNull: { type: { kind: "list" type: { -kind: "reference" -reference: $.Scalars["Int"] +kind: "named" +named: $.Scalars["Int"] } } +namedType: $.Scalars["Int"] args: null } objectNested: { type: { kind: "nullable" type: { -kind: "reference" -reference: Object.ObjectNested +kind: "named" +named: Object.ObjectNested } } +namedType: Object.ObjectNested args: null } objectNonNull: { type: { -kind: "reference" -reference: Object.Object +kind: "named" +named: Object.Object } +namedType: Object.Object args: null } objectWithArgs: { type: { kind: "nullable" type: { -kind: "reference" -reference: Object.Object +kind: "named" +named: Object.Object } } +namedType: Object.Object args: { type: { string?: $.Scalars["String"] | null @@ -195,10 +208,11 @@ fooBarUnion: { type: { kind: "nullable" type: { -kind: "reference" -reference: Union.FooBarUnion +kind: "named" +named: Union.FooBarUnion } } +namedType: Union.FooBarUnion args: null } /** @@ -208,10 +222,11 @@ abcEnum: { type: { kind: "nullable" type: { -kind: "reference" -reference: Enum.ABCEnum +kind: "named" +named: Enum.ABCEnum } } +namedType: Enum.ABCEnum args: null } } @@ -269,6 +284,7 @@ kind: "literal" value: "Foo" } args: null +namedType: "Foo" } /** * Field documentation. @@ -279,10 +295,11 @@ id: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["ID"] +kind: "named" +named: $.Scalars["ID"] } } +namedType: $.Scalars["ID"] args: null } } @@ -294,15 +311,17 @@ kind: "literal" value: "Bar" } args: null +namedType: "Bar" } int: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["Int"] +kind: "named" +named: $.Scalars["Int"] } } +namedType: $.Scalars["Int"] args: null } } @@ -314,25 +333,28 @@ kind: "literal" value: "ObjectNested" } args: null +namedType: "ObjectNested" } id: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["ID"] +kind: "named" +named: $.Scalars["ID"] } } +namedType: $.Scalars["ID"] args: null } object: { type: { kind: "nullable" type: { -kind: "reference" -reference: Object.Object +kind: "named" +named: Object.Object } } +namedType: Object.Object args: null } } @@ -344,55 +366,61 @@ kind: "literal" value: "Object" } args: null +namedType: "Object" } string: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["String"] +kind: "named" +named: $.Scalars["String"] } } +namedType: $.Scalars["String"] args: null } int: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["Int"] +kind: "named" +named: $.Scalars["Int"] } } +namedType: $.Scalars["Int"] args: null } float: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["Float"] +kind: "named" +named: $.Scalars["Float"] } } +namedType: $.Scalars["Float"] args: null } boolean: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["Boolean"] +kind: "named" +named: $.Scalars["Boolean"] } } +namedType: $.Scalars["Boolean"] args: null } id: { type: { kind: "nullable" type: { -kind: "reference" -reference: $.Scalars["ID"] +kind: "named" +named: $.Scalars["ID"] } } +namedType: $.Scalars["ID"] args: null } } From a955a977b5897513f6a67c81e9c87fac33319761 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 25 Feb 2024 22:20:07 -0500 Subject: [PATCH 38/71] list --- src/ResultSet/ResultSet.test-d.ts | 6 +++-- src/ResultSet/ResultSet.ts | 20 ++++++++-------- tests/builder/_/schema.graphql | 3 ++- tests/builder/_/schema.ts | 23 +++++++++++++++++++ .../__snapshots__/generate.test.ts.snap | 23 +++++++++++++++++++ 5 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index c8d27a24f..2ded59037 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -8,7 +8,6 @@ import type { ResultSet } from './__.js' type I = Schema.$.Index type RS<$S extends SelectionSet.Query> = ResultSet.Query<$S, I> -type x = RS<{ object: { id: true } }> // dprint-ignore test(`general`, () => { @@ -43,7 +42,10 @@ test(`general`, () => { // todo // List - expectTypeOf>().toEqualTypeOf<{ listInt: null|number[] }>() + expectTypeOf>().toEqualTypeOf<{ listIntNonNull: number[] }>() + expectTypeOf>().toEqualTypeOf<{ listInt: null|(null|number)[] }>() + expectTypeOf>().toEqualTypeOf<{ listListIntNonNull: number[][] }>() + expectTypeOf>().toEqualTypeOf<{ listListInt: null|((null|(null|number)[])[]) }>() // Alias // scalar diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index 9d3ef8da7..2d51bccb0 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -50,7 +50,7 @@ export type Object<$SelectionSet, $Object extends Schema.Object, $Index extends // dprint-ignore type Union<$SelectionSet, $Node extends Schema.Union, $Index extends Schema.Index> = Values<{ - [$ObjectName in $Node['type']['__typename']['type']]: + [$ObjectName in $Node['type']['__typename']['namedType']]: Object & SelectionSet.UnionOmitFragments<$SelectionSet>, $Index['objects'][$ObjectName], $Index> }> @@ -60,20 +60,20 @@ type Field<$SelectionSet, $Field extends Schema.Field, $Index extends Schema.Ind ( | FieldDirectiveInclude<$SelectionSet> | FieldDirectiveSkip<$SelectionSet> - | FieldUnwrap<$SelectionSet, $Field['type'], $Index> - // | Node<$SelectionSet, $Field['type'], $Index> + | FieldType<$SelectionSet, $Field['type'], $Index> ) // dprint-ignore -type FieldUnwrap< +type FieldType< $SelectionSet, - T extends { kind: 'reference'; reference: any } | { kind: 'list'; type: any } | { kind: 'nullable'; type: any }, + $FieldType extends Schema.FieldTypeNamed | Schema.FieldTypeList | Schema.FieldTypeNullable | Schema.FieldTypeLiteral, $Index extends Schema.Index > = - T extends { kind: 'nullable' } ? null | FieldUnwrap<$SelectionSet,T['type'],$Index> : - T extends { kind: 'list' } ? FieldUnwrap<$SelectionSet,T['type'],$Index>[] : - T extends { kind: 'reference' } ? Node<$SelectionSet, T['reference'], $Index> - : never + $FieldType extends Schema.FieldTypeLiteral ? $FieldType['value'] : + $FieldType extends Schema.FieldTypeNullable ? null | FieldType<$SelectionSet, $FieldType['type'], $Index> : + $FieldType extends Schema.FieldTypeList ? FieldType<$SelectionSet, $FieldType['type'], $Index>[] : + $FieldType extends Schema.FieldTypeNamed ? Node<$SelectionSet, $FieldType['named'], $Index> + : never // dprint-ignore type FieldDirectiveInclude<$SelectionSet> = @@ -95,7 +95,7 @@ export namespace Errors { TSError<'Node', `Unknown case`, { $Node: $Node }> export type UnknownFieldName<$FieldName extends string, $Node extends Schema.Object> = - TSError<'Object', `field "${$FieldName}" does not exist on schema object "${$Node['__typename']['type']}"`> + TSError<'Object', `field "${$FieldName}" does not exist on schema object "${$Node['__typename']['namedType']}"`> } // type SelectField<$Objekt extends Schema.Object, $SelectionObjekt extends SelectionObjekt, $FieldName extends keyof $SelectionObjekt & string> = diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index a7b600491..b30b98d25 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -5,7 +5,8 @@ type Query { stringWithRequiredArg(string:String!): String stringWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): String object: Object - listListInt: [[Int!]!]! + listListIntNonNull: [[Int!]!]! + listListInt: [[Int]] listInt: [Int] listIntNonNull: [Int!]! objectNested: ObjectNested diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index df978d8a4..85fa2f42c 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -120,17 +120,40 @@ named: Object.Object namedType: Object.Object args: null } +listListIntNonNull: { +type: { +kind: "list" +type: { +kind: "list" +type: { +kind: "named" +named: $.Scalars["Int"] +} +} +} +namedType: $.Scalars["Int"] +args: null +} listListInt: { type: { +kind: "nullable" +type: { kind: "list" type: { +kind: "nullable" +type: { kind: "list" type: { +kind: "nullable" +type: { kind: "named" named: $.Scalars["Int"] } } } +} +} +} namedType: $.Scalars["Int"] args: null } diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index a7308693e..f316cc67b 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -123,17 +123,40 @@ named: Object.Object namedType: Object.Object args: null } +listListIntNonNull: { +type: { +kind: "list" +type: { +kind: "list" +type: { +kind: "named" +named: $.Scalars["Int"] +} +} +} +namedType: $.Scalars["Int"] +args: null +} listListInt: { type: { +kind: "nullable" +type: { kind: "list" type: { +kind: "nullable" +type: { kind: "list" type: { +kind: "nullable" +type: { kind: "named" named: $.Scalars["Int"] } } } +} +} +} namedType: $.Scalars["Int"] args: null } From a1d1cea94e0798a8564de6854ba987844a4aba1c Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 25 Feb 2024 23:23:21 -0500 Subject: [PATCH 39/71] interface selection set --- src/ResultSet/ResultSet.test-d.ts | 2 +- src/SelectionSet/SelectionSet.test-d.ts | 154 +++++++++++------- src/SelectionSet/SelectionSet.ts | 47 ++++-- src/generator/generator.ts | 45 +++-- src/lib/graphql.ts | 30 ++-- src/schema/Schema.ts | 4 +- tests/builder/_/schema.graphql | 15 ++ tests/builder/_/schema.ts | 99 ++++++++++- .../__snapshots__/generate.test.ts.snap | 99 ++++++++++- 9 files changed, 386 insertions(+), 109 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 2ded59037..ea098f7b9 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -39,7 +39,7 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | { __typename: "Bar" } | { __typename: "Foo"; id: null|string } }>() // Interface - // todo + expectTypeOf>().toEqualTypeOf<{ interface: { id: null|string } }>() // List expectTypeOf>().toEqualTypeOf<{ listIntNonNull: number[] }>() diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index be2dbf06e..1e1b4bb9e 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -2,7 +2,7 @@ import { assertType, expectTypeOf, test } from 'vitest' import type * as Schema from '../../tests/builder/_/schema.js' import type { SelectionSet } from './__.js' -type S = SelectionSet.Query +type Q = SelectionSet.Query test(`ParseAliasExpression`, () => { expectTypeOf>().toEqualTypeOf>() @@ -14,99 +14,127 @@ test(`ParseAliasExpression`, () => { }) test(`Query`, () => { + // @ts-expect-error directive on root type Query + assertType({ $defer: true }) + // Scalar - assertType({ id: true }) - assertType({ id: false }) - assertType({ id: 1 }) - assertType({ id: 0 }) - assertType({ id: undefined }) + assertType({ id: true }) + assertType({ id: false }) + assertType({ id: 1 }) + assertType({ id: 0 }) + assertType({ id: undefined }) // non-null - assertType({ idNonNull: true }) + assertType({ idNonNull: true }) // Enum - assertType({ abcEnum: true }) + assertType({ abcEnum: true }) // Object - assertType({ object: { id: true } }) + assertType({ object: { id: true } }) // typename - assertType({ __typename: true }) + assertType({ __typename: true }) // Non-Null - assertType({ objectNonNull: { id: true } }) + assertType({ objectNonNull: { id: true } }) // @ts-expect-error excess property check - assertType({ id2: true }) + assertType({ id2: true }) // @ts-expect-error excess property check - assertType({ object: { a2: true } }) + assertType({ object: { a2: true } }) // Union - assertType({ fooBarUnion: { __typename: true } }) - assertType({ fooBarUnion: { onFoo: { __typename: true } } }) - assertType({ fooBarUnion: { onFoo: { id: true } } }) + assertType({ fooBarUnion: { __typename: true } }) + assertType({ fooBarUnion: { onFoo: { __typename: true } } }) + assertType({ fooBarUnion: { onFoo: { id: true } } }) // @ts-expect-error no b - assertType({ fooBarUnion: { onFoo: { id2: true } } }) - assertType({ fooBarUnion: { onBar: { __typename: true } } }) - assertType({ fooBarUnion: { onBar: { int: true } } }) + assertType({ fooBarUnion: { onFoo: { id2: true } } }) + assertType({ fooBarUnion: { onBar: { __typename: true } } }) + assertType({ fooBarUnion: { onBar: { int: true } } }) // @ts-expect-error no a - assertType({ fooBarUnion: { onBar: { int2: true } } }) + assertType({ fooBarUnion: { onBar: { int2: true } } }) + + // Interface + assertType({ interface: { id: true } }) + assertType({ interface: { id: { $defer: true } } }) + assertType({ interface: { id: { $include: true } } }) + assertType({ interface: { id: { $skip: true } } }) + assertType({ interface: { id: { $stream: true } } }) + assertType({ interface: { __typename: true } }) + assertType({ interface: { __typename: { $defer: true } } }) + assertType({ interface: { $scalars: true } }) + // @ts-expect-error needs fragment + assertType({ interface: { id: true, int: true } }) + // @ts-expect-error needs fragment + assertType({ interface: { id: true, boolean: true } }) + assertType({ interface: { id: true, onObject1ImplementingInterface: { int: true } } }) + assertType({ interface: { id: true, onObject2ImplementingInterface: { boolean: true } } }) + // @ts-expect-error incorrect implementor name + assertType({ interface: { id: true, onObject1ImplementingInterface2: { int: true } } }) + // directives work on fragments + assertType({ interface: { id: true, onObject1ImplementingInterface: { $include: true } } }) // todo should REQUIRE field selection // Alias // alias: enum - assertType({ abcEnum_as_enum: true }) + assertType({ abcEnum_as_enum: true }) // alias: object - assertType({ object_as_o: { id: true } }) + assertType({ object_as_o: { id: true } }) // @ts-expect-error invalid alias key format - assertType({ object_as_: { id: true } }) + assertType({ object_as_: { id: true } }) // @ts-expect-error invalid alias key format - assertType({ object_as: { id: true } }) + assertType({ object_as: { id: true } }) // @ts-expect-error invalid alias key format - assertType({ object2_as_o: { id: true } }) + assertType({ object2_as_o: { id: true } }) // directives // @skip // on scalar - assertType({ string: { $skip: true } }) - assertType({ string: { $skip: false } }) - assertType({ string: { $skip: { if: true } } }) - assertType({ string: { $skip: { if: false } } }) - assertType({ string: { $skip: {} } }) - assertType({ string: { $skip: {} } }) + assertType({ string: { $skip: true } }) + assertType({ string: { $skip: false } }) + assertType({ string: { $skip: { if: true } } }) + assertType({ string: { $skip: { if: false } } }) + assertType({ string: { $skip: {} } }) + assertType({ string: { $skip: {} } }) // assertType({ string: skip() }) // on object - assertType({ object: { $skip: true, string: true } }) + assertType({ object: { $skip: true, string: true } }) // assertType({ scalars: skip().select({ a: true }) }) // on fragment - assertType({ fooBarUnion: { onBar: { $skip: true, int: true } } }) + assertType({ fooBarUnion: { onBar: { $skip: true, int: true } } }) // @include - assertType({ string: { $include: true } }) - assertType({ string: { $include: false } }) - assertType({ string: { $include: { if: true } } }) - assertType({ string: { $include: { if: false } } }) - assertType({ string: { $include: {} } }) - assertType({ string: { $include: {} } }) + assertType({ string: { $include: true } }) + assertType({ string: { $include: false } }) + assertType({ string: { $include: { if: true } } }) + assertType({ string: { $include: { if: false } } }) + assertType({ string: { $include: {} } }) + assertType({ string: { $include: {} } }) // assertType({ string: include() }) // @defer - assertType({ string: { $defer: true } }) - assertType({ string: { $defer: { if: true, label: `foo` } } }) - assertType({ string: { $defer: { if: true } } }) - assertType({ string: { $defer: {} } }) + assertType({ string: { $defer: true } }) + assertType({ string: { $defer: { if: true, label: `foo` } } }) + assertType({ string: { $defer: { if: true } } }) + assertType({ string: { $defer: {} } }) // (todo limit to lists?) // @stream - assertType({ string: { $stream: true } }) - assertType({ string: { $stream: { if: true, label: `foo`, initialCount: 0 } } }) - assertType({ string: { $stream: { if: true, label: `foo` } } }) - assertType({ string: { $stream: { if: true } } }) - assertType({ string: { $stream: {} } }) + assertType({ string: { $stream: true } }) + assertType({ string: { $stream: { if: true, label: `foo`, initialCount: 0 } } }) + assertType({ string: { $stream: { if: true, label: `foo` } } }) + assertType({ string: { $stream: { if: true } } }) + assertType({ string: { $stream: {} } }) // Field Group - assertType({ object: { ___: { $skip: true, int: true, id: true } } }) - assertType({ object: { ___: [{ $skip: true, int: true, id: true }] } }) + // On Object + assertType({ object: { ___: { int: true, id: true } } }) + assertType({ object: { ___: { $skip: true, int: true, id: true } } }) + assertType({ object: { ___: [{ $skip: true, int: true, id: true }] } }) + // On Root (Query) + assertType({ ___: { id: true } }) + assertType({ ___: { $skip: true, id: true } }) // Arguments // all-optional on object - assertType({ objectWithArgs: { $: {}, id: true } }) - assertType({ objectWithArgs: { id: true } }) - assertType({ + assertType({ objectWithArgs: { $: {}, id: true } }) + assertType({ objectWithArgs: { id: true } }) + assertType({ objectWithArgs: { $: { boolean: true, @@ -121,9 +149,9 @@ test(`Query`, () => { // builder interface // assertType({ foo: args({ ... }) }) // all-optional on scalar - assertType({ stringWithArgs: true }) - assertType({ stringWithArgs: {} }) - assertType({ + assertType({ stringWithArgs: true }) + assertType({ stringWithArgs: {} }) + assertType({ stringWithArgs: { $: { boolean: true, @@ -135,23 +163,23 @@ test(`Query`, () => { }, }) // all-optional + scalar + directive - assertType({ stringWithArgs: { $: { boolean: true }, $skip: true } }) + assertType({ stringWithArgs: { $: { boolean: true }, $skip: true } }) // builder interface // assertType({ foo: args({ boolean: true }).skip().select({ x: 1 }) }) // 1+ required + scalar - assertType({ stringWithRequiredArg: { $: { string: `` } } }) + assertType({ stringWithRequiredArg: { $: { string: `` } } }) // @ts-expect-error missing "string" arg - assertType({ stringWithRequiredArg: { $: {} } }) + assertType({ stringWithRequiredArg: { $: {} } }) // @ts-expect-error missing args ("$") - assertType({ stringWithRequiredArg: {} }) + assertType({ stringWithRequiredArg: {} }) // Scalars Wildcard ("client directive") // object - assertType({ object: { $scalars: true } }) + assertType({ object: { $scalars: true } }) // @ts-expect-error no directives on scalars field - assertType({ scalars: { $scalars: { $skip: true } } }) + assertType({ scalars: { $scalars: { $skip: true } } }) // union fragment - assertType({ fooBarUnion: { onBar: { $scalars: true } } }) + assertType({ fooBarUnion: { onBar: { $scalars: true } } }) // assertType({ scalars: select() }) // todo empty selection set not allowed, with arguments given diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index bbcbcfd1f..5b3ec1e93 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -37,14 +37,13 @@ type Object< ]?: Field, $Index> } - & FieldDirectives & /** * Inline fragments for field groups. * @see https://spec.graphql.org/draft/#sec-Inline-Fragments */ { - ___?: MaybeList> + ___?: MaybeList & FieldDirectives> } & /** @@ -64,18 +63,9 @@ export type Field< $Field extends Schema.FieldTypename ? NoArgsIndicator : $Field extends Schema.FieldScalar ? Indicator<$Field> : $Field extends Schema.FieldUnion ? Union<$Field['namedType'], $Index> : - $Field extends Schema.FieldObject ? Object<$Field['namedType'], $Index> & Arguments<$Field> : - TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> - -// type UnwrapListAndNullableFieldType<$Field extends Schema.Field> = UnwrapListAndNullableFieldType_<$Field['type']> - -// // dprint-ignore -// type UnwrapListAndNullableFieldType_ = -// FT extends Schema.FieldTypeList ? UnwrapListAndNullableFieldType_ : -// FT extends Schema.FieldTypeNullable ? UnwrapListAndNullableFieldType_ : -// FT extends Schema.FieldTypeLiteral ? FT : -// FT extends Schema.FieldTypeNamed ? FT -// : TSError<'UnwrapFieldType_', 'FT case not handled', { FT: FT }> + $Field extends Schema.FieldInterface ? Interface$<$Field['namedType'], $Index> : + $Field extends Schema.FieldObject ? Object<$Field['namedType'], $Index> & Arguments<$Field> & FieldDirectives + : TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.FieldArgs ? $Field['args']['allOptional'] extends true ? { @@ -86,6 +76,23 @@ type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.Fiel } : {} +// dprint-ignore +type Interface$< + $Interface extends Schema.Interface$, + $Index extends Schema.Index, +> = +& Object< + & $Interface['type'] + & { + __typename: $Interface['implementors']['__typename'] + }, + $Index + > +& { + [Key in $Interface['implementors']['__typename']['namedType'] as `on${Capitalize}`]?: + Object, $Index> & FieldDirectives + } + // TODO why does $object not get passed to this in a distributed way? // dprint-ignore type Union< @@ -94,7 +101,7 @@ type Union< > = & { [Key in $Union['type']['__typename']['namedType'] as `on${Capitalize}`]?: - Object, $Index> + Object, $Index> & FieldDirectives } & { __typename?: NoArgsIndicator @@ -210,3 +217,13 @@ export interface FieldDirectives { */ $stream?: boolean | { if?: boolean; label?: string; initialCount?: number } } + +// type UnwrapListAndNullableFieldType<$Field extends Schema.Field> = UnwrapListAndNullableFieldType_<$Field['type']> + +// // dprint-ignore +// type UnwrapListAndNullableFieldType_ = +// FT extends Schema.FieldTypeList ? UnwrapListAndNullableFieldType_ : +// FT extends Schema.FieldTypeNullable ? UnwrapListAndNullableFieldType_ : +// FT extends Schema.FieldTypeLiteral ? FT : +// FT extends Schema.FieldTypeNamed ? FT +// : TSError<'UnwrapFieldType_', 'FT case not handled', { FT: FT }> diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 5a9256057..91e54e35a 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -4,13 +4,21 @@ import type { GraphQLInputObjectType, GraphQLInterfaceType, GraphQLNamedType, + GraphQLSchema, } from 'graphql' import { GraphQLNonNull, GraphQLObjectType, isEnumType, isListType, isNamedType, isNonNullType } from 'graphql' import { buildSchema } from 'graphql' import _ from 'json-bigint' import fs from 'node:fs/promises' import { Code } from '../lib/Code.js' -import type { AnyClass, AnyField, AnyNamedClassName, Describable, NameToClassNamedType } from '../lib/graphql.js' +import type { + AnyClass, + AnyField, + AnyNamedClassName, + Describable, + NameToClassNamedType, + TypeMapByKind, +} from '../lib/graphql.js' import { getNodeDisplayName, getTypeMapByKind, @@ -123,11 +131,22 @@ const concreteRenderers = defineConcreteRenderers({ getDocumentation(config, node), Code.export$(Code.interface$(node.name, renderFields(config, node))), ), - GraphQLInterfaceType: (config, node) => - Code.TSDoc( + GraphQLInterfaceType: (config, node) => { + const implementors = config.typeMapByKind.GraphQLObjectType.filter(_ => + _.getInterfaces().filter(_ => _.name === node.name).length > 0 + ) + return Code.TSDoc( getDocumentation(config, node), - Code.export$(Code.interface$(node.name, renderFields(config, node))), - ), + Code.export$(Code.interface$( + node.name, + Code.objectFrom({ + __interfacename: Code.quote(node.name), + type: renderFields(config, node), + implementors: Code.unionItems(implementors.map(_ => dispatchToReferenceRenderer(config, _))), + }), + )), + ) + }, GraphQLObjectType: (config, node) => Code.TSDoc( getDocumentation(config, node), @@ -141,7 +160,7 @@ const concreteRenderers = defineConcreteRenderers({ Code.interface$( node.name, Code.objectFrom({ - __unionname: { type: Code.quote(node.name) }, + __unionname: Code.quote(node.name), type: { type: Code.unionItems( node @@ -339,23 +358,27 @@ interface Input { } interface Config { + schema: GraphQLSchema + typeMapByKind: TypeMapByKind TSDoc: { noDocPolicy: 'message' | 'ignore' } } -const resolveOptions = (options: Input['options']): Config => { +const resolveOptions = (input: Input): Config => { + const schema = buildSchema(input.schemaSource) return { + schema, + typeMapByKind: getTypeMapByKind(schema), TSDoc: { - noDocPolicy: options?.TSDoc?.noDocPolicy ?? `ignore`, + noDocPolicy: input.options?.TSDoc?.noDocPolicy ?? `ignore`, }, } } export const generateCode = (input: Input) => { - const schema = buildSchema(input.schemaSource) - const typeMapByKind = getTypeMapByKind(schema) - const config = resolveOptions(input.options) + const config = resolveOptions(input) + const { typeMapByKind } = config const hasQuery = typeMapByKind.GraphQLRootTypes.find( (_) => _.name === `Query`, diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index af0ae6d9b..31efb8d3b 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -1,4 +1,4 @@ -import type { EnumValueNode, GraphQLEnumValue, GraphQLField, GraphQLInputField, GraphQLSchema } from 'graphql' +import type { GraphQLEnumValue, GraphQLField, GraphQLInputField, GraphQLSchema } from 'graphql' import { GraphQLEnumType, GraphQLInputObjectType, @@ -10,22 +10,24 @@ import { GraphQLUnionType, } from 'graphql' +export type TypeMapByKind = + & { + [Name in keyof NameToClassNamedType]: InstanceType[] + } + & { GraphQLRootTypes: GraphQLObjectType[] } + export const getTypeMapByKind = (schema: GraphQLSchema) => { const typeMap = schema.getTypeMap() const typeMapValues = Object.values(typeMap) - const typeMapByKind: - & { - [Name in keyof NameToClassNamedType]: InstanceType[] - } - & { GraphQLRootTypes: GraphQLObjectType[] } = { - GraphQLRootTypes: [], - GraphQLScalarType: [], - GraphQLEnumType: [], - GraphQLInputObjectType: [], - GraphQLInterfaceType: [], - GraphQLObjectType: [], - GraphQLUnionType: [], - } + const typeMapByKind: TypeMapByKind = { + GraphQLRootTypes: [], + GraphQLScalarType: [], + GraphQLEnumType: [], + GraphQLInputObjectType: [], + GraphQLInterfaceType: [], + GraphQLObjectType: [], + GraphQLUnionType: [], + } for (const type of typeMapValues) { if (type.name.startsWith(`__`)) continue switch (true) { diff --git a/src/schema/Schema.ts b/src/schema/Schema.ts index 5862f150f..77a2ea024 100644 --- a/src/schema/Schema.ts +++ b/src/schema/Schema.ts @@ -21,8 +21,9 @@ export interface Index { export type Scalar = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] export type Object = { __typename: FieldTypename } export type Union = { __unionname: string; type: Object } +export type Interface$ = { __interfacename: string; type: object; implementors: Object } export type Literal = string -export type Named = Scalar | Object | Union | Literal +export type Named = Scalar | Object | Union | Literal | Interface$ export type Node = Object | Union | Scalar // | Nullable export type FieldTypeNamed = { kind: 'named'; named: any } @@ -39,6 +40,7 @@ export type Field<$Named extends Named = Named> = { export type FieldScalar = Field export type FieldObject = Field export type FieldUnion = Field +export type FieldInterface = Field export type FieldTypename = { args: null; type: FieldTypeLiteral; namedType: string } export type FieldArgs = { type: object; allOptional: boolean } diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index b30b98d25..665a13a11 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -1,4 +1,5 @@ type Query { + interface: Interface id: ID idNonNull: ID! string: String @@ -51,6 +52,20 @@ type Object { id: ID } +interface Interface { + id: ID +} + +type Object1ImplementingInterface implements Interface { + id: ID + int: Int +} + +type Object2ImplementingInterface implements Interface { + id: ID + boolean: Boolean +} + """ Enum documentation. """ diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 85fa2f42c..a9a2c357f 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -10,6 +10,8 @@ Foo: Object.Foo Bar: Object.Bar ObjectNested: Object.ObjectNested Object: Object.Object +Object1ImplementingInterface: Object.Object1ImplementingInterface +Object2ImplementingInterface: Object.Object2ImplementingInterface } unionMemberNames: { FooBarUnion: "Foo" @@ -43,6 +45,17 @@ value: "Query" args: null namedType: "Query" } +interface: { +type: { +kind: "nullable" +type: { +kind: "named" +named: Interface.Interface +} +} +namedType: Interface.Interface +args: null +} id: { type: { kind: "nullable" @@ -285,8 +298,24 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { -// -- no types -- - +export interface Interface { +__interfacename: "Interface" +type: { +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +} +implementors: Object.Object1ImplementingInterface +| Object.Object2ImplementingInterface +} } // ------------------------------------------------------------ // @@ -444,6 +473,72 @@ namedType: $.Scalars["ID"] args: null } } + +export interface Object1ImplementingInterface { +__typename: { +type: { +kind: "literal" +value: "Object1ImplementingInterface" +} +args: null +namedType: "Object1ImplementingInterface" +} +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +int: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["Int"] +} +} +namedType: $.Scalars["Int"] +args: null +} +} + +export interface Object2ImplementingInterface { +__typename: { +type: { +kind: "literal" +value: "Object2ImplementingInterface" +} +args: null +namedType: "Object2ImplementingInterface" +} +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +boolean: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["Boolean"] +} +} +namedType: $.Scalars["Boolean"] +args: null +} +} } // ------------------------------------------------------------ // diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index f316cc67b..adc7212c7 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -13,6 +13,8 @@ Foo: Object.Foo Bar: Object.Bar ObjectNested: Object.ObjectNested Object: Object.Object +Object1ImplementingInterface: Object.Object1ImplementingInterface +Object2ImplementingInterface: Object.Object2ImplementingInterface } unionMemberNames: { FooBarUnion: "Foo" @@ -46,6 +48,17 @@ value: "Query" args: null namedType: "Query" } +interface: { +type: { +kind: "nullable" +type: { +kind: "named" +named: Interface.Interface +} +} +namedType: Interface.Interface +args: null +} id: { type: { kind: "nullable" @@ -288,8 +301,24 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { -// -- no types -- - +export interface Interface { +__interfacename: "Interface" +type: { +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +} +implementors: Object.Object1ImplementingInterface +| Object.Object2ImplementingInterface +} } // ------------------------------------------------------------ // @@ -447,6 +476,72 @@ namedType: $.Scalars["ID"] args: null } } + +export interface Object1ImplementingInterface { +__typename: { +type: { +kind: "literal" +value: "Object1ImplementingInterface" +} +args: null +namedType: "Object1ImplementingInterface" +} +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +int: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["Int"] +} +} +namedType: $.Scalars["Int"] +args: null +} +} + +export interface Object2ImplementingInterface { +__typename: { +type: { +kind: "literal" +value: "Object2ImplementingInterface" +} +args: null +namedType: "Object2ImplementingInterface" +} +id: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["ID"] +} +} +namedType: $.Scalars["ID"] +args: null +} +boolean: { +type: { +kind: "nullable" +type: { +kind: "named" +named: $.Scalars["Boolean"] +} +} +namedType: $.Scalars["Boolean"] +args: null +} +} } // ------------------------------------------------------------ // From bddb2f21c5ea5d6268a40cd40b8ef8781e5a362d Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 25 Feb 2024 23:39:23 -0500 Subject: [PATCH 40/71] interface result set --- src/ResultSet/ResultSet.test-d.ts | 8 +++++++- src/ResultSet/ResultSet.ts | 12 ++++++++++++ src/SelectionSet/SelectionSet.ts | 10 +++++----- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index ea098f7b9..af504621e 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -39,7 +39,13 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | { __typename: "Bar" } | { __typename: "Foo"; id: null|string } }>() // Interface - expectTypeOf>().toEqualTypeOf<{ interface: { id: null|string } }>() + expectTypeOf>().toEqualTypeOf<{ interface: null | { id: null | string} | {} }>() + expectTypeOf>().toEqualTypeOf<{ interface: null | { int: null | number} | {} }>() + expectTypeOf>().toEqualTypeOf<{ interface: null | { id: null | string} }>() + expectTypeOf>().toEqualTypeOf<{ interface: null | { id: null | string} }>() + expectTypeOf>().toEqualTypeOf<{ interface: null | { id: null | string} | { id: null | string; int: null | number }}>() + expectTypeOf>().toEqualTypeOf<{ interface: null | { __typename: 'Object1ImplementingInterface' } | { __typename: 'Object2ImplementingInterface' } }>() + expectTypeOf>().toEqualTypeOf<{ interface: null | { __typename: 'Object1ImplementingInterface' } | {}}>() // List expectTypeOf>().toEqualTypeOf<{ listIntNonNull: number[] }>() diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index 2d51bccb0..aa94f56c8 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -20,6 +20,8 @@ export type Subscription<$SelectionSetSubscription extends object, $Index extend // dprint-ignore type Node<$SelectionSet, $Node extends Schema.Node, $Index extends Schema.Index> = $Node extends Schema.Union ? Union<$SelectionSet, $Node, $Index> : + $Node extends Schema.Interface$ ? Interface$<$SelectionSet, $Node, $Index> : + // todo handle types where each union member implements the same interface -- this should yield support for both interface and union features $Node extends Schema.Object ? Object<$SelectionSet, $Node, $Index> : $Node extends Schema.Scalar ? $Node : Errors.UnknownNode<$Node> @@ -54,6 +56,16 @@ type Union<$SelectionSet, $Node extends Schema.Union, $Index extends Schema.Inde Object & SelectionSet.UnionOmitFragments<$SelectionSet>, $Index['objects'][$ObjectName], $Index> }> +type Interface$<$SelectionSet, $Node extends Schema.Interface$, $Index extends Schema.Index> = Values< + { + [$ObjectName in $Node['implementors']['__typename']['namedType']]: Object< + GetKeyOr<$SelectionSet, `on${$ObjectName}`, {}> & SelectionSet.UnionOmitFragments<$SelectionSet>, + $Index['objects'][$ObjectName], + $Index + > + } +> + // dprint-ignore type Field<$SelectionSet, $Field extends Schema.Field, $Index extends Schema.Index> = $SelectionSet extends SelectionSet.Directive.Include.Negative | SelectionSet.Directive.Skip.Positive ? null : diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 5b3ec1e93..fef9bdeb3 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -78,19 +78,19 @@ type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.Fiel // dprint-ignore type Interface$< - $Interface extends Schema.Interface$, + $Node extends Schema.Interface$, $Index extends Schema.Index, > = & Object< - & $Interface['type'] + & $Node['type'] & { - __typename: $Interface['implementors']['__typename'] + __typename: $Node['implementors']['__typename'] }, $Index > & { - [Key in $Interface['implementors']['__typename']['namedType'] as `on${Capitalize}`]?: - Object, $Index> & FieldDirectives + [Key in $Node['implementors']['__typename']['namedType'] as `on${Capitalize}`]?: + Object, $Index> & FieldDirectives } // TODO why does $object not get passed to this in a distributed way? From bd9690ee98a76e7b64063e3031d6b6eba81a3d2c Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 25 Feb 2024 23:41:59 -0500 Subject: [PATCH 41/71] scalars etst --- src/ResultSet/ResultSet.test-d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index af504621e..f2c858497 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -46,6 +46,7 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ interface: null | { id: null | string} | { id: null | string; int: null | number }}>() expectTypeOf>().toEqualTypeOf<{ interface: null | { __typename: 'Object1ImplementingInterface' } | { __typename: 'Object2ImplementingInterface' } }>() expectTypeOf>().toEqualTypeOf<{ interface: null | { __typename: 'Object1ImplementingInterface' } | {}}>() + expectTypeOf>().toEqualTypeOf<{ interface: null | { __typename: 'Object1ImplementingInterface', id: null | string, int: null|number} | { __typename: 'Object2ImplementingInterface', id: null | string; boolean:null|boolean} }>() // List expectTypeOf>().toEqualTypeOf<{ listIntNonNull: number[] }>() From 8690d1cf14622f298c9091ba583c5b79a47f7085 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 25 Feb 2024 23:42:46 -0500 Subject: [PATCH 42/71] tidy --- src/ResultSet/ResultSet.test-d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index f2c858497..239a7d29e 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -6,7 +6,6 @@ import type { SelectionSet } from '../SelectionSet/__.js' import type { ResultSet } from './__.js' type I = Schema.$.Index - type RS<$S extends SelectionSet.Query> = ResultSet.Query<$S, I> // dprint-ignore From 65d3c52a8bcd0dbc0c86ab04486d48970d2f1e31 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Mon, 26 Feb 2024 20:17:51 -0500 Subject: [PATCH 43/71] rename --- src/{schema => Schema2}/Schema.test-d.ts | 0 src/{schema => Schema2}/Schema.ts | 0 src/{schema => Schema2}/__.ts | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/{schema => Schema2}/Schema.test-d.ts (100%) rename src/{schema => Schema2}/Schema.ts (100%) rename src/{schema => Schema2}/__.ts (100%) diff --git a/src/schema/Schema.test-d.ts b/src/Schema2/Schema.test-d.ts similarity index 100% rename from src/schema/Schema.test-d.ts rename to src/Schema2/Schema.test-d.ts diff --git a/src/schema/Schema.ts b/src/Schema2/Schema.ts similarity index 100% rename from src/schema/Schema.ts rename to src/Schema2/Schema.ts diff --git a/src/schema/__.ts b/src/Schema2/__.ts similarity index 100% rename from src/schema/__.ts rename to src/Schema2/__.ts From df97f198b1d703207b86f679f0e737f89efde204 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Mon, 26 Feb 2024 20:18:30 -0500 Subject: [PATCH 44/71] remove demo --- src/demo.ts | 1471 --------------------------------------------------- 1 file changed, 1471 deletions(-) delete mode 100644 src/demo.ts diff --git a/src/demo.ts b/src/demo.ts deleted file mode 100644 index 739b013b4..000000000 --- a/src/demo.ts +++ /dev/null @@ -1,1471 +0,0 @@ -export namespace $ { - export interface Scalars { - Boolean: boolean - Int: number - String: string - ID: string - Date: string - Float: number - } -} - -// ------------------------------------------------------------ // -// Root // -// ------------------------------------------------------------ // - -export namespace Root { - export interface Mutation { - accelerateCachePurge: - | Object.ErrorInternal - | Object.SideEffectConfirmation - accelerateDisable: - | Object.ErrorInternal - | Object.SideEffectConfirmation - accelerateEnable: - | Object.ErrorInternal - | Object.SideEffectConfirmation - databaseLinkCreate: - | Object.DatabaseLink - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - databaseLinkDelete: - | Object.DatabaseLinkNode - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - databaseLinkUpdate: - | Object.DatabaseLink - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - environmentCreate: - | Object.Environment - | Object.ErrorInternal - | Object.ErrorUserBusinessPlanLimitHit - | Object.ErrorUserBusinessResourceNotFound - environmentDelete: - | Object.Environment - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - environmentUpdate: - | Object.Environment - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - projectCreate: - | Object.ErrorInternal - | Object.ErrorUserBusinessPlanLimitHit - | Object.ErrorUserBusinessResourceNotFound - | Object.Project - projectDelete: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ProjectNode - projectUpdate: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.Project - pulseDisable: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.SideEffectConfirmation - pulseEnable: - | Object.ErrorInternal - | Object.ErrorUser - | Object.SideEffectConfirmation - serviceKeyCreate: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ServiceKeyWithValue - serviceKeyDelete: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ServiceKeyNode - userUpdate: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.User - userUpdateDefaultWorkspace: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.User - workspaceCreate: - | Object.ErrorInternal - | Object.Workspace - workspaceDelete: - | Object.ErrorInternal - | Object.ErrorUserBusinessDeleteWorkspaceOnPaidPlan - | Object.ErrorUserBusinessResourceNotFound - | Object.WorkspaceNode - workspaceMembershipCreate: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ErrorUserBusinessUserAlreadyMemberOfOrganization - | Object.WorkspaceMembership - workspaceMembershipDelete: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.WorkspaceMembershipNode - workspacePlanSubscriptionChange: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.PlanSubscription - workspaceUpdate: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.Workspace - workspaceUpdateBillingAddress: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.Workspace - workspaceUpdateBillingEmail: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.Workspace - } - - export interface Query { - /** - * testing - */ - environment: - | Object.Environment - | Object.ErrorInternal - | Object.ErrorUserBusinessNotAuthorized - | Object.ErrorUserBusinessResourceNotFound - me: Object.Me - plan: - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ErrorUserInput - | Object.Plan - project: - | Object.ErrorInternal - | Object.ErrorUserBusinessNotAuthorized - | Object.ErrorUserBusinessResourceNotFound - | Object.Project - serviceKeys: Array - system: Object.System - user: Object.User - workspace: - | Object.ErrorInternal - | Object.ErrorUserBusinessNotAuthorized - | Object.ErrorUserBusinessResourceNotFound - | Object.Workspace - } -} - -// ------------------------------------------------------------ // -// Enum // -// ------------------------------------------------------------ // - -export namespace Enum { - export type CountryCode = - | 'AD' - | 'AE' - | 'AF' - | 'AG' - | 'AI' - | 'AL' - | 'AM' - | 'AO' - | 'AQ' - | 'AR' - | 'AS' - | 'AT' - | 'AU' - | 'AW' - | 'AX' - | 'AZ' - | 'BA' - | 'BB' - | 'BD' - | 'BE' - | 'BF' - | 'BG' - | 'BH' - | 'BI' - | 'BJ' - | 'BL' - | 'BM' - | 'BN' - | 'BO' - | 'BQ' - | 'BR' - | 'BS' - | 'BT' - | 'BV' - | 'BW' - | 'BY' - | 'BZ' - | 'CA' - | 'CC' - | 'CD' - | 'CF' - | 'CG' - | 'CH' - | 'CI' - | 'CK' - | 'CL' - | 'CM' - | 'CN' - | 'CO' - | 'CR' - | 'CU' - | 'CV' - | 'CW' - | 'CX' - | 'CY' - | 'CZ' - | 'DE' - | 'DJ' - | 'DK' - | 'DM' - | 'DO' - | 'DZ' - | 'EC' - | 'EE' - | 'EG' - | 'EH' - | 'ER' - | 'ES' - | 'ET' - | 'FI' - | 'FJ' - | 'FK' - | 'FM' - | 'FO' - | 'FR' - | 'GA' - | 'GB' - | 'GD' - | 'GE' - | 'GF' - | 'GG' - | 'GH' - | 'GI' - | 'GL' - | 'GM' - | 'GN' - | 'GP' - | 'GQ' - | 'GR' - | 'GS' - | 'GT' - | 'GU' - | 'GW' - | 'GY' - | 'HK' - | 'HM' - | 'HN' - | 'HR' - | 'HT' - | 'HU' - | 'ID' - | 'IE' - | 'IL' - | 'IM' - | 'IN' - | 'IO' - | 'IQ' - | 'IR' - | 'IS' - | 'IT' - | 'JE' - | 'JM' - | 'JO' - | 'JP' - | 'KE' - | 'KG' - | 'KH' - | 'KI' - | 'KM' - | 'KN' - | 'KP' - | 'KR' - | 'KW' - | 'KY' - | 'KZ' - | 'LA' - | 'LB' - | 'LC' - | 'LI' - | 'LK' - | 'LR' - | 'LS' - | 'LT' - | 'LU' - | 'LV' - | 'LY' - | 'MA' - | 'MC' - | 'MD' - | 'ME' - | 'MF' - | 'MG' - | 'MH' - | 'MK' - | 'ML' - | 'MM' - | 'MN' - | 'MO' - | 'MP' - | 'MQ' - | 'MR' - | 'MS' - | 'MT' - | 'MU' - | 'MV' - | 'MW' - | 'MX' - | 'MY' - | 'MZ' - | 'NA' - | 'NC' - | 'NE' - | 'NF' - | 'NG' - | 'NI' - | 'NL' - | 'NO' - | 'NP' - | 'NR' - | 'NU' - | 'NZ' - | 'OM' - | 'PA' - | 'PE' - | 'PF' - | 'PG' - | 'PH' - | 'PK' - | 'PL' - | 'PM' - | 'PN' - | 'PR' - | 'PS' - | 'PT' - | 'PW' - | 'PY' - | 'QA' - | 'RE' - | 'RO' - | 'RS' - | 'RU' - | 'RW' - | 'SA' - | 'SB' - | 'SC' - | 'SD' - | 'SE' - | 'SG' - | 'SH' - | 'SI' - | 'SJ' - | 'SK' - | 'SL' - | 'SM' - | 'SN' - | 'SO' - | 'SR' - | 'SS' - | 'ST' - | 'SV' - | 'SX' - | 'SY' - | 'SZ' - | 'TC' - | 'TD' - | 'TF' - | 'TG' - | 'TH' - | 'TJ' - | 'TK' - | 'TL' - | 'TM' - | 'TN' - | 'TO' - | 'TR' - | 'TT' - | 'TV' - | 'TW' - | 'TZ' - | 'UA' - | 'UG' - | 'UM' - | 'US' - | 'UY' - | 'UZ' - | 'VA' - | 'VC' - | 'VE' - | 'VG' - | 'VI' - | 'VN' - | 'VU' - | 'WF' - | 'WS' - | 'YE' - | 'YT' - | 'ZA' - | 'ZM' - | 'ZW' - - export type EnvironmentAccelerateUsageTimeWindowInput = - | 'last6h' - | 'last7d' - | 'last24h' - | 'last30d' - | 'last30m' - - export type FeatureHandle = - | 'accelerateEgress' - | 'acceleratePurgeCache' - | 'accelerateQuery' - | 'access' - | 'createProject' - | 'organizationRole' - | 'platformSupport' - - export type FeatureResourceAggregationValueResolverType = 'count' - - export type MetricUnit = - | 'ms' - | 'percent' - - export type NumberPredicateFnType = - | 'NumberPredicateFnEQ' - | 'NumberPredicateFnGT' - | 'NumberPredicateFnGTE' - | 'NumberPredicateFnLT' - | 'NumberPredicateFnLTE' - - export type Order = - | 'asc' - | 'desc' - - export type PaymentMethodCardBrand = - | 'amex' - | 'diners' - | 'discover' - | 'eftpos_au' - | 'jcb' - | 'mastercard' - | 'unionpay' - | 'unknown' - | 'visa' - - export type PreviousDateHandle = - | 'last6h' - | 'last7d' - | 'last24h' - | 'last30d' - | 'last30m' - | 'startOfCycle' - - export type ResourceType = - | 'Project' - | 'Workspace' - - export type StorageUnit = 'bytes' - - export type WorkspaceRole = - | 'accountant' - | 'admin' - | 'developer' - | 'viewer' -} - -// ------------------------------------------------------------ // -// InputObject // -// ------------------------------------------------------------ // - -export namespace InputObject { - export interface MutationAccelerateCachePurgeInput { - environmentId: $.Scalars['ID'] - } - - export interface MutationAccelerateDisableInput { - environmentId: $.Scalars['ID'] - } - - export interface MutationAccelerateEnableInput { - databaseLinkId: $.Scalars['ID'] - } - - export interface MutationDatabaseLinkCreateInput { - connectionString: $.Scalars['String'] - displayName: $.Scalars['String'] | null - environmentId: $.Scalars['ID'] - regionId: $.Scalars['String'] | null - } - - export interface MutationDatabaseLinkDeleteInput { - id: $.Scalars['ID'] - } - - export interface MutationDatabaseLinkUpdateInput { - connectionString: $.Scalars['String'] - id: $.Scalars['ID'] - regionId: $.Scalars['String'] - } - - export interface MutationEnvironmentCreateInput { - displayName: $.Scalars['String'] | null - isDefault: $.Scalars['Boolean'] | null - projectId: $.Scalars['ID'] - } - - export interface MutationEnvironmentDeleteInput { - id: $.Scalars['ID'] - } - - export interface MutationEnvironmentUpdateInput { - displayName: $.Scalars['String'] | null - id: $.Scalars['ID'] - isDefault: $.Scalars['Boolean'] | null - } - - export interface MutationProjectCreateInput { - displayName: $.Scalars['String'] | null - workspaceId: $.Scalars['ID'] - } - - export interface MutationProjectDeleteInput { - id: $.Scalars['ID'] - } - - export interface MutationProjectUpdateInput { - displayName: $.Scalars['String'] | null - id: $.Scalars['ID'] - } - - export interface MutationPulseDisableInput { - environmentId: $.Scalars['String'] - } - - export interface MutationPulseEnableInput { - databaseLinkId: $.Scalars['String'] - } - - export interface MutationServiceKeyCreateInput { - displayName: $.Scalars['String'] | null - environmentId: $.Scalars['ID'] - } - - export interface MutationServiceKeyDeleteInput { - id: $.Scalars['String'] - } - - export interface MutationUserUpdateDefaultWorkspaceInput { - workspaceId: $.Scalars['ID'] - } - - export interface MutationUserUpdateInput { - displayName: $.Scalars['String'] | null - id: $.Scalars['ID'] - } - - export interface MutationWorkspaceCreateInput { - displayName: $.Scalars['String'] | null - } - - export interface MutationWorkspaceDeleteInput { - id: $.Scalars['ID'] - } - - export interface MutationWorkspaceMembershipCreateInput { - email: $.Scalars['String'] - role: Enum.WorkspaceRole - workspaceId: $.Scalars['ID'] - } - - export interface MutationWorkspaceMembershipDeleteInput { - id: $.Scalars['ID'] - } - - export interface MutationWorkspacePlanSubscriptionChangeInput { - targetPlanId: $.Scalars['ID'] - workspaceId: $.Scalars['ID'] - } - - export interface MutationWorkspaceUpdateBillingAddressInput { - address: InputObject.PhysicalAddressInput - id: $.Scalars['ID'] - } - - export interface MutationWorkspaceUpdateBillingEmailInput { - email: $.Scalars['String'] - id: $.Scalars['ID'] - } - - export interface MutationWorkspaceUpdateInput { - displayName: $.Scalars['String'] | null - id: $.Scalars['ID'] - } - - export interface PhysicalAddressInput { - addressLine1: $.Scalars['String'] | null - addressLine2: $.Scalars['String'] | null - city: $.Scalars['String'] | null - country: Enum.CountryCode | null - postalCodeOrZIP: $.Scalars['String'] | null - region: $.Scalars['String'] | null - } - - export interface TimeIntervalInput { - fromDate: $.Scalars['Date'] | null - fromDateHandle: Enum.PreviousDateHandle | null - toDate: $.Scalars['Date'] | null - } - - export interface WorkspaceOrderBy { - displayName: Enum.Order | null - } -} - -// ------------------------------------------------------------ // -// Interface // -// ------------------------------------------------------------ // - -export namespace Interface { - export interface Error { - message: $.Scalars['String'] - } - - export interface Feature { - displayName: $.Scalars['String'] | null - handle: Enum.FeatureHandle - id: $.Scalars['ID'] - stripeProductId: $.Scalars['String'] - } - - export interface Node { - id: $.Scalars['String'] - } - - export interface Offer { - context: - | Object.Plan - | Object.PlanSubscription - id: $.Scalars['ID'] - price: - | Object.PriceConstant - | Object.PriceTiered - | null - } - - export interface PriceI { - id: $.Scalars['String'] - stripePriceId: $.Scalars['ID'] - } - - export interface ProductStatus { - enabled: $.Scalars['Boolean'] - } -} - -// ------------------------------------------------------------ // -// Object // -// ------------------------------------------------------------ // - -export namespace Object { - export interface AccelerateStatusDisabled { - enabled: $.Scalars['Boolean'] - } - - export interface AccelerateStatusEnabled { - enabled: $.Scalars['Boolean'] - } - - export interface Count { - number: $.Scalars['Int'] - } - - export interface DatabaseLink { - connectionStringHint: $.Scalars['String'] - id: $.Scalars['ID'] - protocol: $.Scalars['String'] - region: $.Scalars['String'] | null - } - - export interface DatabaseLinkNode { - connectionStringHint: $.Scalars['ID'] - displayName: $.Scalars['String'] - id: $.Scalars['String'] - } - - export interface Environment { - accelerate: Object.EnvironmentAccelerate - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - id: $.Scalars['ID'] - isDefault: $.Scalars['Boolean'] - project: Object.Project - pulse: Object.EnvironmentPulse - serviceKeys: Array - tenantId: $.Scalars['ID'] - } - - export interface EnvironmentAccelerate { - /** - * Nullable. - */ - databaseLink: Object.DatabaseLink | null - holds: Array - status: - | Object.AccelerateStatusDisabled - | Object.AccelerateStatusEnabled - usage: Object.EnvironmentAccelerateUsage - } - - export interface EnvironmentAccelerateTimeSeriesPoints { - queries: Object.EnvironmentAccelerateUsageTimeSeriesPointsQueries - timestamps: Array<$.Scalars['Date']> - } - - export interface EnvironmentAccelerateUsage { - latency: Object.EnvironmentAccelerateUsageLatency - overview: Object.EnvironmentAccelerateUsageOverview - timeInterval: Object.TimeInterval - timeSeries: Object.EnvironmentAccelerateUsageTimeSeries - } - - export interface EnvironmentAccelerateUsageLatency { - queries: Object.EnvironmentAccelerateUsageLatencyQueries - } - - export interface EnvironmentAccelerateUsageLatencyQueries { - cached: Object.EnvironmentAccelerateUsageLatencyQuery - origin: Object.EnvironmentAccelerateUsageLatencyQuery - } - - export interface EnvironmentAccelerateUsageLatencyQuery { - count: Object.Count - durationAverage: Object.MetricValue - durationPercentiles: Array - } - - export interface EnvironmentAccelerateUsageOverview { - egress: Object.EnvironmentAccelerateUsageOverviewEgress - queries: Object.EnvironmentAccelerateUsageOverviewQueries - } - - export interface EnvironmentAccelerateUsageOverviewCacheHit { - ratioToMiss: Object.MetricValue - } - - export interface EnvironmentAccelerateUsageOverviewEgress { - averageResponseSize: Object.StorageValue - requestsServedFromOrigin: Object.Count - total: Object.StorageValue - } - - export interface EnvironmentAccelerateUsageOverviewQueries { - cacheHit: Object.EnvironmentAccelerateUsageOverviewCacheHit - cacheableCount: Object.Count - totalCount: Object.Count - } - - export interface EnvironmentAccelerateUsageTimeSeries { - points: Object.EnvironmentAccelerateTimeSeriesPoints - } - - export interface EnvironmentAccelerateUsageTimeSeriesPointsQueries { - miss: Array | null - none: Array | null - swr: Array | null - ttl: Array | null - } - - export interface EnvironmentAccelerateUsageTimeSeriesPointsQuery { - count: Object.Count - timestamp: $.Scalars['Date'] - } - - export interface EnvironmentPulse { - databaseLink: Object.DatabaseLink | null - status: - | Object.PulseStatusDisabled - | Object.PulseStatusEnabled - } - - export interface ErrorInternal { - message: $.Scalars['String'] - } - - export interface ErrorUser { - message: $.Scalars['String'] - } - - export interface ErrorUserBusinessDeleteWorkspaceOnPaidPlan { - context: Object.ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext - message: $.Scalars['String'] - } - - export interface ErrorUserBusinessDeleteWorkspaceOnPaidPlanContext { - plan: Object.Plan - } - - export interface ErrorUserBusinessNotAuthorized { - message: $.Scalars['String'] - } - - export interface ErrorUserBusinessPlanLimitHit { - context: Object.ErrorUserBusinessPlanLimitHitContext - message: $.Scalars['String'] - } - - export interface ErrorUserBusinessPlanLimitHitContext { - featureHandle: $.Scalars['String'] | null - } - - export interface ErrorUserBusinessResourceNotFound { - context: Object.ErrorUserBusinessResourceNotFoundContext - message: $.Scalars['String'] - } - - export interface ErrorUserBusinessResourceNotFoundContext { - id: $.Scalars['ID'] | null - typeName: Enum.ResourceType - } - - export interface ErrorUserBusinessUserAlreadyMemberOfOrganization { - context: Object.ErrorUserBusinessUserAlreadyMemberOfOrganizationContext - message: $.Scalars['String'] - } - - export interface ErrorUserBusinessUserAlreadyMemberOfOrganizationContext { - user: Object.User - workspace: Object.Workspace - } - - export interface ErrorUserInput { - message: $.Scalars['String'] - } - - export interface FeatureAbstract { - displayName: $.Scalars['String'] | null - handle: Enum.FeatureHandle - id: $.Scalars['ID'] - stripeProductId: $.Scalars['String'] - } - - export interface FeatureResourceAggregation { - displayName: $.Scalars['String'] | null - handle: Enum.FeatureHandle - id: $.Scalars['ID'] - resource: $.Scalars['String'] - scope: $.Scalars['String'] - stripeProductId: $.Scalars['String'] - valueResolver: Object.FeatureResourceAggregationValueResolver - } - - export interface FeatureResourceAggregationValueResolver { - type: Enum.FeatureResourceAggregationValueResolverType - } - - export interface FeatureResourceProperty { - displayName: $.Scalars['String'] | null - handle: Enum.FeatureHandle - id: $.Scalars['ID'] - resource: $.Scalars['String'] - scope: $.Scalars['String'] - stripeProductId: $.Scalars['String'] - valueResolver: Object.FeatureResourcePropertyValueResolver - } - - export interface FeatureResourcePropertyValueResolver { - field: $.Scalars['String'] - type: - | Object.FeatureValueTypeBoolean - | Object.FeatureValueTypeEnum - | Object.FeatureValueTypeNumber - | Object.FeatureValueTypeString - } - - export interface FeatureValue { - displayName: $.Scalars['String'] | null - handle: Enum.FeatureHandle - id: $.Scalars['ID'] - stripeProductId: $.Scalars['String'] - valueType: - | Object.FeatureValueTypeBoolean - | Object.FeatureValueTypeEnum - | Object.FeatureValueTypeNumber - | Object.FeatureValueTypeString - } - - export interface FeatureValueTypeBoolean { - displayName: $.Scalars['String'] - } - - export interface FeatureValueTypeEnum { - displayName: $.Scalars['String'] - members: Array - } - - export interface FeatureValueTypeEnumMember { - description: $.Scalars['String'] | null - value: $.Scalars['String'] - } - - export interface FeatureValueTypeNumber { - displayName: $.Scalars['String'] - } - - export interface FeatureValueTypeString { - displayName: $.Scalars['String'] - } - - export interface LimitEnum { - allowed: Array<$.Scalars['String']> - } - - export interface LimitNumber { - amount: $.Scalars['Int'] - type: Enum.NumberPredicateFnType - } - - export interface Me { - user: Object.User - workspaces: Array - } - - export interface MetricValue { - number: $.Scalars['Float'] - unit: Enum.MetricUnit | null - } - - export interface OfferAbstract { - context: - | Object.Plan - | Object.PlanSubscription - feature: Object.FeatureAbstract - id: $.Scalars['ID'] - price: - | Object.PriceConstant - | Object.PriceTiered - | null - } - - export interface OfferResourceAggregation { - context: - | Object.Plan - | Object.PlanSubscription - feature: Object.FeatureResourceAggregation - id: $.Scalars['ID'] - limit: - | Object.LimitEnum - | Object.LimitNumber - | null - price: - | Object.PriceConstant - | Object.PriceTiered - | null - timeInterval: - | Object.OfferTimeIntervalCycle - | Object.OfferTimeIntervalPrevious - | null - } - - export interface OfferResourceProperty { - context: - | Object.Plan - | Object.PlanSubscription - feature: Object.FeatureResourceProperty - id: $.Scalars['ID'] - limit: - | Object.LimitEnum - | Object.LimitNumber - | null - price: - | Object.PriceConstant - | Object.PriceTiered - | null - timeInterval: - | Object.OfferTimeIntervalCycle - | Object.OfferTimeIntervalPrevious - | null - } - - export interface OfferTimeIntervalCycle { - ok: $.Scalars['Boolean'] - } - - export interface OfferTimeIntervalPrevious { - milliseconds: $.Scalars['Int'] - } - - export interface OfferValue { - context: - | Object.Plan - | Object.PlanSubscription - feature: Object.FeatureValue - id: $.Scalars['ID'] - limit: - | Object.LimitEnum - | Object.LimitNumber - | null - price: - | Object.PriceConstant - | Object.PriceTiered - | null - value: $.Scalars['String'] - } - - export interface PaymentMethod { - card: Object.PaymentMethodCard - id: $.Scalars['ID'] - isDefault: $.Scalars['Boolean'] - } - - export interface PaymentMethodCard { - brand: Enum.PaymentMethodCardBrand - expiryMonth: $.Scalars['Int'] - expiryYear: $.Scalars['Int'] - id: $.Scalars['ID'] - last4: $.Scalars['String'] - } - - export interface Percentile { - percentile: $.Scalars['Int'] - value: Object.MetricValue - } - - export interface PhysicalAddress { - addressLine1: $.Scalars['String'] | null - addressLine2: $.Scalars['String'] | null - city: $.Scalars['String'] | null - country: $.Scalars['String'] | null - postalCodeOrZIP: $.Scalars['String'] | null - region: $.Scalars['String'] | null - } - - export interface Plan { - displayName: $.Scalars['String'] - handle: $.Scalars['String'] - id: $.Scalars['ID'] - isDefault: $.Scalars['Boolean'] - isFree: $.Scalars['Boolean'] - offers: Object.PlanOffers - power: $.Scalars['Int'] - selectable: $.Scalars['Boolean'] - version: $.Scalars['Int'] - versionIsLatest: $.Scalars['Boolean'] - versions: Object.PlanVersions - } - - export interface PlanOffers { - accelerate: Object.PlanOffersAccelerate - conductor: Object.PlanOffersConductor - platform: Object.PlanOffersPlatform - } - - export interface PlanOffersAccelerate { - egress: Object.OfferResourceProperty - purgeCache: Object.OfferResourceAggregation - query: Object.OfferResourceAggregation - } - - export interface PlanOffersConductor { - createProject: Object.OfferResourceAggregation - organizationRole: Object.OfferResourceProperty - } - - export interface PlanOffersPlatform { - access: Object.OfferAbstract - support: Object.OfferValue - } - - export interface PlanSubscription { - createdAt: $.Scalars['Date'] - id: $.Scalars['ID'] - plan: Object.Plan - stripeSubscriptionId: $.Scalars['String'] | null - stripeSubscriptionLineItems: Array - workspace: Object.Workspace - } - - export interface PlanVersions { - isLatest: $.Scalars['Boolean'] - next: Array - previous: Array - } - - export interface PriceConstant { - cents: $.Scalars['Int'] - id: $.Scalars['String'] - stripePriceId: $.Scalars['ID'] - } - - export interface PriceTiered { - id: $.Scalars['String'] - stripePriceId: $.Scalars['ID'] - tiers: Array - } - - export interface PriceTieredTier { - cents: $.Scalars['Float'] - from: $.Scalars['Int'] - to: $.Scalars['Int'] | null - } - - export interface ProductHold { - createdAt: $.Scalars['Int'] - expiresAt: $.Scalars['Int'] - reason: $.Scalars['String'] - } - - export interface Project { - accelerate: Object.EnvironmentAccelerate - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - environments: Array - id: $.Scalars['ID'] - pulse: Object.EnvironmentPulse - workspace: Object.Workspace - } - - export interface ProjectNode { - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - id: $.Scalars['String'] - workspaceId: $.Scalars['ID'] - } - - export interface PulseStatusDisabled { - enabled: $.Scalars['Boolean'] - } - - export interface PulseStatusEnabled { - enabled: $.Scalars['Boolean'] - error: $.Scalars['String'] | null - } - - export interface ServiceKey { - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - id: $.Scalars['ID'] - valueHint: $.Scalars['String'] - } - - export interface ServiceKeyNode { - displayName: $.Scalars['String'] - id: $.Scalars['String'] - valueHint: $.Scalars['String'] - } - - export interface ServiceKeyWithValue { - serviceKey: Object.ServiceKey - value: $.Scalars['ID'] - } - - export interface SideEffectConfirmation { - ok: $.Scalars['Boolean'] - } - - export interface StorageValue { - number: $.Scalars['Float'] - unit: Enum.StorageUnit | null - } - - export interface StripeSubscriptionLineItem { - feature: Enum.FeatureHandle - id: $.Scalars['ID'] - } - - export interface System { - accelerate: Object.SystemAccelerate - plans: Array - pulse: Object.SystemPulse - } - - export interface SystemAccelerate { - defaultRegion: Object.SystemAccelerateRegion - regions: Array - } - - export interface SystemAccelerateRegion { - displayName: $.Scalars['String'] - id: $.Scalars['ID'] - } - - export interface SystemPulse { - defaultRegion: Object.SystemAccelerateRegion - regions: Array - } - - export interface TimeInterval { - from: $.Scalars['Date'] - to: $.Scalars['Date'] - } - - export interface UsageProductAccelerate { - egress: Object.UsageProductAccelerateFeatureEgress - request: Object.UsageProductAccelerateFeatureRequest - } - - export interface UsageProductAccelerateFeatureEgress { - averageResponseSize: $.Scalars['Float'] - total: $.Scalars['Float'] - } - - export interface UsageProductAccelerateFeatureRequest { - all: Object.UsageProductAccelerateFeatureRequestFilterAll - cacheHit: Object.UsageProductAccelerateFeatureRequestFilterCacheHit - } - - export interface UsageProductAccelerateFeatureRequestFilterAll { - count: $.Scalars['Int'] - } - - export interface UsageProductAccelerateFeatureRequestFilterCacheHit { - ratioToMiss: $.Scalars['Int'] - } - - export interface User { - displayName: $.Scalars['String'] | null - email: $.Scalars['String'] - featureFlags: Object.UserFeatureFlags - handle: $.Scalars['String'] | null - id: $.Scalars['ID'] - image: $.Scalars['String'] | null - preferences: Object.UserPreferences - } - - export interface UserFeatureFlags { - adminDashboard: $.Scalars['Boolean'] - mars: $.Scalars['Boolean'] - mercury: $.Scalars['Boolean'] - venus: $.Scalars['Boolean'] - } - - export interface UserPreferences { - defaultWorkspace: Object.Workspace | null - } - - export interface Workspace { - billingAddress: Object.PhysicalAddress | null - billingEmail: $.Scalars['String'] - createdAt: $.Scalars['Date'] - displayName: $.Scalars['String'] - id: $.Scalars['ID'] - isUsersLastMembership: $.Scalars['Boolean'] - memberships: Array - paymentMethods: Array - planSubscription: Object.PlanSubscription - projects: Array - stripeCustomerId: $.Scalars['String'] - usage: Object.WorkspaceUsage - } - - export interface WorkspaceMembership { - id: $.Scalars['ID'] - role: Enum.WorkspaceRole - user: Object.User - } - - export interface WorkspaceMembershipNode { - id: $.Scalars['String'] - workspaceId: $.Scalars['ID'] - } - - export interface WorkspaceNode { - billingEmail: $.Scalars['String'] - displayName: $.Scalars['String'] - id: $.Scalars['String'] - } - - export interface WorkspaceUsage { - accelerate: Object.UsageProductAccelerate - timeInterval: Object.TimeInterval - } -} - -// ------------------------------------------------------------ // -// Union // -// ------------------------------------------------------------ // - -export namespace Union { - export type AccelerateStatus = - | Object.AccelerateStatusDisabled - | Object.AccelerateStatusEnabled - - export type FeatureValueType = - | Object.FeatureValueTypeBoolean - | Object.FeatureValueTypeEnum - | Object.FeatureValueTypeNumber - | Object.FeatureValueTypeString - - export type Limit = - | Object.LimitEnum - | Object.LimitNumber - - export type MutationAccelerateCachePurgeResult = - | Object.ErrorInternal - | Object.SideEffectConfirmation - - export type MutationAccelerateDisableResult = - | Object.ErrorInternal - | Object.SideEffectConfirmation - - export type MutationAccelerateEnableResult = - | Object.ErrorInternal - | Object.SideEffectConfirmation - - export type MutationDatabaseLinkCreateResult = - | Object.DatabaseLink - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - - export type MutationDatabaseLinkDeleteResult = - | Object.DatabaseLinkNode - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - - export type MutationDatabaseLinkUpdateResult = - | Object.DatabaseLink - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - - export type MutationEnvironmentCreateResult = - | Object.Environment - | Object.ErrorInternal - | Object.ErrorUserBusinessPlanLimitHit - | Object.ErrorUserBusinessResourceNotFound - - export type MutationEnvironmentDeleteResult = - | Object.Environment - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - - export type MutationEnvironmentUpdateResult = - | Object.Environment - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - - export type MutationProjectCreateResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessPlanLimitHit - | Object.ErrorUserBusinessResourceNotFound - | Object.Project - - export type MutationProjectDeleteResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ProjectNode - - export type MutationProjectUpdateResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.Project - - export type MutationPulseDisableResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.SideEffectConfirmation - - export type MutationPulseEnableResult = - | Object.ErrorInternal - | Object.ErrorUser - | Object.SideEffectConfirmation - - export type MutationServiceKeyCreateResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ServiceKeyWithValue - - export type MutationServiceKeyDeleteResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ServiceKeyNode - - export type MutationUserUpdateDefaultWorkspaceResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.User - - export type MutationUserUpdateResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.User - - export type MutationWorkspaceCreateResult = - | Object.ErrorInternal - | Object.Workspace - - export type MutationWorkspaceDeleteResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessDeleteWorkspaceOnPaidPlan - | Object.ErrorUserBusinessResourceNotFound - | Object.WorkspaceNode - - export type MutationWorkspaceMembershipCreateResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ErrorUserBusinessUserAlreadyMemberOfOrganization - | Object.WorkspaceMembership - - export type MutationWorkspaceMembershipDeleteResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.WorkspaceMembershipNode - - export type MutationWorkspacePlanSubscriptionChangeResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.PlanSubscription - - export type MutationWorkspaceUpdateBillingAddressResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.Workspace - - export type MutationWorkspaceUpdateBillingEmailResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.Workspace - - export type MutationWorkspaceUpdateResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.Workspace - - export type OfferContext = - | Object.Plan - | Object.PlanSubscription - - export type OfferTimeInterval = - | Object.OfferTimeIntervalCycle - | Object.OfferTimeIntervalPrevious - - export type Price = - | Object.PriceConstant - | Object.PriceTiered - - export type PulseStatus = - | Object.PulseStatusDisabled - | Object.PulseStatusEnabled - - export type QueryEnvironmentResult = - | Object.Environment - | Object.ErrorInternal - | Object.ErrorUserBusinessNotAuthorized - | Object.ErrorUserBusinessResourceNotFound - - export type QueryPlanResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessResourceNotFound - | Object.ErrorUserInput - | Object.Plan - - export type QueryProjectResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessNotAuthorized - | Object.ErrorUserBusinessResourceNotFound - | Object.Project - - export type QueryWorkspaceResult = - | Object.ErrorInternal - | Object.ErrorUserBusinessNotAuthorized - | Object.ErrorUserBusinessResourceNotFound - | Object.Workspace -} From 15b9d9b67e10537351d4f2e44c8fdba65da04dfe Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Mon, 26 Feb 2024 20:19:30 -0500 Subject: [PATCH 45/71] fix --- src/ResultSet/ResultSet.ts | 2 +- src/SelectionSet/SelectionSet.ts | 2 +- src/lib/client.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index aa94f56c8..42736fa50 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -2,7 +2,7 @@ import type { GetKeyOr, SimplifyDeep, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' -import type { Schema } from '../schema/__.js' +import type { Schema } from '../Schema2/__.js' import type { SelectionSet } from '../SelectionSet/__.js' // dprint-ignore diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index fef9bdeb3..d7bc5dad9 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -2,7 +2,7 @@ import type { MaybeList, StringNonEmpty, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' -import type { Schema } from '../schema/__.js' +import type { Schema } from '../Schema2/__.js' export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Object ? Object<$Index['Root']['Query'], $Index> diff --git a/src/lib/client.ts b/src/lib/client.ts index 1dc914641..d366ac02e 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -1,5 +1,5 @@ import type { ResultSet } from '../ResultSet/__.js' -import type { Index } from '../schema/Schema.js' +import type { Index } from '../Schema2/Schema.js' import type { SelectionSet } from '../SelectionSet/__.js' // dprint-ignore From 4b758cbb4d8b3b839ee0231920fdd595b1af42e3 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Mon, 26 Feb 2024 20:20:19 -0500 Subject: [PATCH 46/71] rename --- src/ResultSet/ResultSet.ts | 2 +- src/{Schema2 => Schema}/Schema.test-d.ts | 0 src/{Schema2 => Schema}/Schema.ts | 0 src/{Schema2 => Schema}/__.ts | 0 src/SelectionSet/SelectionSet.ts | 2 +- src/lib/client.ts | 2 +- 6 files changed, 3 insertions(+), 3 deletions(-) rename src/{Schema2 => Schema}/Schema.test-d.ts (100%) rename src/{Schema2 => Schema}/Schema.ts (100%) rename src/{Schema2 => Schema}/__.ts (100%) diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index 42736fa50..2705a778a 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -2,7 +2,7 @@ import type { GetKeyOr, SimplifyDeep, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' -import type { Schema } from '../Schema2/__.js' +import type { Schema } from '../Schema/__.js' import type { SelectionSet } from '../SelectionSet/__.js' // dprint-ignore diff --git a/src/Schema2/Schema.test-d.ts b/src/Schema/Schema.test-d.ts similarity index 100% rename from src/Schema2/Schema.test-d.ts rename to src/Schema/Schema.test-d.ts diff --git a/src/Schema2/Schema.ts b/src/Schema/Schema.ts similarity index 100% rename from src/Schema2/Schema.ts rename to src/Schema/Schema.ts diff --git a/src/Schema2/__.ts b/src/Schema/__.ts similarity index 100% rename from src/Schema2/__.ts rename to src/Schema/__.ts diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index d7bc5dad9..4fd0efa2b 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -2,7 +2,7 @@ import type { MaybeList, StringNonEmpty, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' -import type { Schema } from '../Schema2/__.js' +import type { Schema } from '../Schema/__.js' export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Object ? Object<$Index['Root']['Query'], $Index> diff --git a/src/lib/client.ts b/src/lib/client.ts index d366ac02e..6c5e62809 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -1,5 +1,5 @@ import type { ResultSet } from '../ResultSet/__.js' -import type { Index } from '../Schema2/Schema.js' +import type { Index } from '../Schema/Schema.js' import type { SelectionSet } from '../SelectionSet/__.js' // dprint-ignore From 63bd71e29f5011c4e749bce2110aaa83dc6d94f7 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Mon, 26 Feb 2024 22:07:58 -0500 Subject: [PATCH 47/71] transformer --- src/SelectionSet/SelectionSet.ts | 2 +- .../toGraphQLDocumentString.test.ts.snap | 346 ++++++++++++++++++ .../toGraphQLDocumentString.test.ts | 43 +++ src/SelectionSet/toGraphQLDocumentString.ts | 86 +++++ src/{lib => }/client.ts | 7 +- 5 files changed, 479 insertions(+), 5 deletions(-) create mode 100644 src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap create mode 100644 src/SelectionSet/toGraphQLDocumentString.test.ts create mode 100644 src/SelectionSet/toGraphQLDocumentString.ts rename src/{lib => }/client.ts (83%) diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 4fd0efa2b..19a8779d1 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -190,7 +190,7 @@ export type OmitNegativeIndicators<$SelectionSet> = { export type NoArgsIndicator = ClientIndicator | FieldDirectives // dprint-ignore -export type Indicator<$Field extends Schema.FieldScalar> = +export type Indicator<$Field extends Schema.FieldScalar = Schema.FieldScalar> = $Field['args'] extends Schema.FieldArgs ? $Field['args']['allOptional'] extends true ? ({ $?: $Field['args']['type'] } & FieldDirectives) | ClientIndicator : { $: $Field['args']['type'] } & FieldDirectives diff --git a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap new file mode 100644 index 000000000..564b27312 --- /dev/null +++ b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap @@ -0,0 +1,346 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Query 1`] = ` +" +{ + "string": true +} +-------------- +{ + string +} +" +`; + +exports[`Query 2`] = ` +" +{ + "string": 1 +} +-------------- +{ + string +} +" +`; + +exports[`Query 3`] = ` +" +{ + "id": true, + "string": false +} +-------------- +{ + id +} +" +`; + +exports[`Query 4`] = ` +" +{ + "id": true, + "string": 0 +} +-------------- +{ + id +} +" +`; + +exports[`Query 5`] = ` +" +{ + "id": true +} +-------------- +{ + id +} +" +`; + +exports[`Query 6`] = ` +" +{ + "object": { + "id": true + } +} +-------------- +{ + object { + id + } +} +" +`; + +exports[`Query 7`] = ` +" +{ + "objectNested": { + "object": { + "string": true, + "id": true, + "int": false + } + } +} +-------------- +{ + objectNested { + object { + string + id + } + } +} +" +`; + +exports[`Query 8`] = ` +" +{ + "object": { + "$include": true, + "id": true + } +} +-------------- +{ + object @include(if: true) { + id + } +} +" +`; + +exports[`Query 9`] = ` +" +{ + "object": { + "$include": false, + "id": true + } +} +-------------- +{ + object @include(if: false) { + id + } +} +" +`; + +exports[`Query 10`] = ` +" +{ + "object": { + "id": true + } +} +-------------- +{ + object { + id + } +} +" +`; + +exports[`Query 11`] = ` +" +{ + "object": { + "$include": { + "if": true + }, + "id": true + } +} +-------------- +{ + object @include(if: true) { + id + } +} +" +`; + +exports[`Query 12`] = ` +" +{ + "object": { + "$include": { + "if": false + }, + "id": true + } +} +-------------- +{ + object @include(if: false) { + id + } +} +" +`; + +exports[`Query 13`] = ` +" +{ + "object": { + "$include": {}, + "id": true + } +} +-------------- +{ + object @include(if: true) { + id + } +} +" +`; + +exports[`Query 14`] = ` +" +{ + "object": { + "$include": {}, + "id": true + } +} +-------------- +{ + object @include(if: true) { + id + } +} +" +`; + +exports[`Query 15`] = ` +" +{ + "object": { + "$skip": true, + "id": true + } +} +-------------- +{ + object @skip(if: true) { + id + } +} +" +`; + +exports[`Query 16`] = ` +" +{ + "object": { + "$skip": false, + "id": true + } +} +-------------- +{ + object @skip(if: false) { + id + } +} +" +`; + +exports[`Query 17`] = ` +" +{ + "object": { + "id": true + } +} +-------------- +{ + object { + id + } +} +" +`; + +exports[`Query 18`] = ` +" +{ + "object": { + "$skip": { + "if": true + }, + "id": true + } +} +-------------- +{ + object @skip(if: true) { + id + } +} +" +`; + +exports[`Query 19`] = ` +" +{ + "object": { + "$skip": { + "if": false + }, + "id": true + } +} +-------------- +{ + object @skip(if: false) { + id + } +} +" +`; + +exports[`Query 20`] = ` +" +{ + "object": { + "$skip": {}, + "id": true + } +} +-------------- +{ + object @skip(if: true) { + id + } +} +" +`; + +exports[`Query 21`] = ` +" +{ + "object": { + "$skip": {}, + "id": true + } +} +-------------- +{ + object @skip(if: true) { + id + } +} +" +`; diff --git a/src/SelectionSet/toGraphQLDocumentString.test.ts b/src/SelectionSet/toGraphQLDocumentString.test.ts new file mode 100644 index 000000000..5abc1edc7 --- /dev/null +++ b/src/SelectionSet/toGraphQLDocumentString.test.ts @@ -0,0 +1,43 @@ +import { parse, print } from 'graphql' +import { expect, test } from 'vitest' +import type * as Schema from '../../tests/builder/_/schema.js' +import type { SelectionSet } from './__.js' +import { toGraphQLDocumentString } from './toGraphQLDocumentString.js' + +type Q = SelectionSet.Query +const s = (selectionSet: Q) => selectionSet + +test.each([ + s({ string: true }), + s({ string: 1 }), + // s({ string: false }), // todo should be static error + s({ id: true, string: false }), + s({ id: true, string: 0 }), + s({ id: true, string: undefined }), + s({ object: { id: true } }), + s({ objectNested: { object: { string: true, id: true, int: false } } }), + // $include + s({ object: { $include: true, id: true } }), + s({ object: { $include: false, id: true } }), + s({ object: { $include: undefined, id: true } }), + s({ object: { $include: { if: true }, id: true } }), + s({ object: { $include: { if: false }, id: true } }), + s({ object: { $include: { if: undefined }, id: true } }), + s({ object: { $include: {}, id: true } }), + // $skip + s({ object: { $skip: true, id: true } }), + s({ object: { $skip: false, id: true } }), + s({ object: { $skip: undefined, id: true } }), + s({ object: { $skip: { if: true }, id: true } }), + s({ object: { $skip: { if: false }, id: true } }), + s({ object: { $skip: { if: undefined }, id: true } }), + s({ object: { $skip: {}, id: true } }), +])(`Query`, (ss) => { + const graphqlDocumentString = toGraphQLDocumentString(ss) + // Should parse, ensures is syntactically valid graphql document. + const document = parse(graphqlDocumentString) + const graphqlDocumentStringFormatted = print(document) + const beforeAfter = `\n` + JSON.stringify(ss, null, 2) + + `\n--------------\n` + graphqlDocumentStringFormatted + `\n` + expect(beforeAfter).toMatchSnapshot() +}) diff --git a/src/SelectionSet/toGraphQLDocumentString.ts b/src/SelectionSet/toGraphQLDocumentString.ts new file mode 100644 index 000000000..12acda4e4 --- /dev/null +++ b/src/SelectionSet/toGraphQLDocumentString.ts @@ -0,0 +1,86 @@ +import type { SelectionSet } from './__.js' + +type SpecialFields = { + // todo + // $scalars?: SelectionSet.Indicator + // $?: Record + $include?: SelectionSet.Directive.Include['$include'] + $skip?: SelectionSet.Directive.Skip['$skip'] +} + +// const specialFields = { +// $include: `$include`, +// $skip: `$skip`, +// $scalars: `$scalars`, +// $: `$`, +// } + +type SS = { + [k: string]: SS_ +} & SpecialFields + +type SS_ = { + [k: string]: SelectionSet.Indicator | SS_ +} & SpecialFields + +export const toGraphQLDocumentString = (ss: {}) => { + let docString = `` + docString += `query { + ${selectionSet(ss)} + }` + return docString +} + +const indicatorOrSelectionSet = (ss: SelectionSet.Indicator | SS): string => { + if (isIndicator(ss)) return `` + + const { $include, $skip, ...rest } = ss + + let directives = `` + + if ($include !== undefined) { + directives += `@include(if: ${ + typeof $include === `boolean` ? $include : $include.if === undefined ? true : $include.if + })` + } + + if ($skip !== undefined) { + directives += `@skip(if: ${typeof $skip === `boolean` ? $skip : $skip.if === undefined ? true : $skip.if})` + } + + return `${directives} { + ${selectionSet(rest)} + }` +} + +const selectionSet = (ss: SS) => { + return Object.entries(ss).filter(([k, v]) => { + return isPositiveIndicator(v) + }).map(([field, ss]) => { + return `${field} ${indicatorOrSelectionSet(ss)}` + }).join(`\n`) + `\n` +} + +const isIndicator = (v: any): v is SelectionSet.Indicator => { + return String(v) in indicator +} + +const isPositiveIndicator = (v: any): v is SelectionSet.ClientIndicatorPositive => { + return !(String(v) in negativeIndicator) +} + +const negativeIndicator = { + '0': 0, + 'false': false, + 'undefined': undefined, +} + +const positiveIndicator = { + '1': 1, + 'true': true, +} + +const indicator = { + ...negativeIndicator, + ...positiveIndicator, +} diff --git a/src/lib/client.ts b/src/client.ts similarity index 83% rename from src/lib/client.ts rename to src/client.ts index 6c5e62809..73cb75b69 100644 --- a/src/lib/client.ts +++ b/src/client.ts @@ -1,6 +1,6 @@ -import type { ResultSet } from '../ResultSet/__.js' -import type { Index } from '../Schema/Schema.js' -import type { SelectionSet } from '../SelectionSet/__.js' +import type { ResultSet } from './ResultSet/__.js' +import type { Index } from './Schema/Schema.js' +import type { SelectionSet } from './SelectionSet/__.js' // dprint-ignore export type Client<$SchemaIndex extends Index> = @@ -22,5 +22,4 @@ interface Input { } export const create = <$SchemaIndex extends Index>(input: Input): Client<$SchemaIndex> => { - return 1 as any } From 26e9c23917b98d3fea87c4f97569a9e5a0064de1 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Tue, 27 Feb 2024 22:31:12 -0500 Subject: [PATCH 48/71] no prettier --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index eafd9269a..f25c05b6f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,6 @@ "source.addMissingImports": "explicit", "source.fixAll.eslint": "explicit", "source.organizeImports": "never" - } + }, + "prettier.enable": false } From 24a94f13fc9315f61ddd0fa19e1a79baf6aa293d Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Tue, 27 Feb 2024 22:42:16 -0500 Subject: [PATCH 49/71] begin args --- .../toGraphQLDocumentString.test.ts.snap | 219 ++++++++++-------- .../toGraphQLDocumentString.test.ts | 92 +++++--- src/SelectionSet/toGraphQLDocumentString.ts | 22 +- 3 files changed, 199 insertions(+), 134 deletions(-) diff --git a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap index 564b27312..298be0a14 100644 --- a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap +++ b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap @@ -1,141 +1,162 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Query 1`] = ` +exports[`$include > Query 1`] = ` " { - "string": true + "object": { + "$include": true, + "id": true + } } -------------- { - string + object @include(if: true) { + id + } } " `; -exports[`Query 2`] = ` +exports[`$include > Query 2`] = ` " { - "string": 1 + "object": { + "$include": false, + "id": true + } } -------------- { - string + object @include(if: false) { + id + } } " `; -exports[`Query 3`] = ` +exports[`$include > Query 3`] = ` " { - "id": true, - "string": false + "object": { + "id": true + } } -------------- { - id + object { + id + } } " `; -exports[`Query 4`] = ` +exports[`$include > Query 4`] = ` " { - "id": true, - "string": 0 + "object": { + "$include": { + "if": true + }, + "id": true + } } -------------- { - id + object @include(if: true) { + id + } } " `; -exports[`Query 5`] = ` +exports[`$include > Query 5`] = ` " { - "id": true + "object": { + "$include": { + "if": false + }, + "id": true + } } -------------- { - id + object @include(if: false) { + id + } } " `; -exports[`Query 6`] = ` +exports[`$include > Query 6`] = ` " { "object": { + "$include": {}, "id": true } } -------------- { - object { + object @include(if: true) { id } } " `; -exports[`Query 7`] = ` +exports[`$include > Query 7`] = ` " { - "objectNested": { - "object": { - "string": true, - "id": true, - "int": false - } + "object": { + "$include": {}, + "id": true } } -------------- { - objectNested { - object { - string - id - } + object @include(if: true) { + id } } " `; -exports[`Query 8`] = ` +exports[`$skip > Query 1`] = ` " { "object": { - "$include": true, + "$skip": true, "id": true } } -------------- { - object @include(if: true) { + object @skip(if: true) { id } } " `; -exports[`Query 9`] = ` +exports[`$skip > Query 2`] = ` " { "object": { - "$include": false, + "$skip": false, "id": true } } -------------- { - object @include(if: false) { + object @skip(if: false) { id } } " `; -exports[`Query 10`] = ` +exports[`$skip > Query 3`] = ` " { "object": { @@ -151,11 +172,11 @@ exports[`Query 10`] = ` " `; -exports[`Query 11`] = ` +exports[`$skip > Query 4`] = ` " { "object": { - "$include": { + "$skip": { "if": true }, "id": true @@ -163,18 +184,18 @@ exports[`Query 11`] = ` } -------------- { - object @include(if: true) { + object @skip(if: true) { id } } " `; -exports[`Query 12`] = ` +exports[`$skip > Query 5`] = ` " { "object": { - "$include": { + "$skip": { "if": false }, "id": true @@ -182,164 +203,160 @@ exports[`Query 12`] = ` } -------------- { - object @include(if: false) { + object @skip(if: false) { id } } " `; -exports[`Query 13`] = ` +exports[`$skip > Query 6`] = ` " { "object": { - "$include": {}, + "$skip": {}, "id": true } } -------------- { - object @include(if: true) { + object @skip(if: true) { id } } " `; -exports[`Query 14`] = ` +exports[`$skip > Query 7`] = ` " { "object": { - "$include": {}, + "$skip": {}, "id": true } } -------------- { - object @include(if: true) { + object @skip(if: true) { id } } " `; -exports[`Query 15`] = ` +exports[`args > Query 1`] = ` " { - "object": { - "$skip": true, - "id": true + "stringWithArgs": { + "$": { + "boolean": true, + "float": 1 + } } } -------------- { - object @skip(if: true) { - id - } + stringWithArgs(boolean: true, float: 1) } " `; -exports[`Query 16`] = ` +exports[`other > Query 1`] = ` " { - "object": { - "$skip": false, - "id": true - } + "string": true } -------------- { - object @skip(if: false) { - id - } + string } " `; -exports[`Query 17`] = ` +exports[`other > Query 2`] = ` " { - "object": { - "id": true - } + "string": 1 } -------------- { - object { - id - } + string } " `; -exports[`Query 18`] = ` +exports[`other > Query 3`] = ` " { - "object": { - "$skip": { - "if": true - }, - "id": true - } + "id": true, + "string": false } -------------- { - object @skip(if: true) { - id - } + id } " `; -exports[`Query 19`] = ` +exports[`other > Query 4`] = ` " { - "object": { - "$skip": { - "if": false - }, - "id": true - } + "id": true, + "string": 0 } -------------- { - object @skip(if: false) { - id - } + id +} +" +`; + +exports[`other > Query 5`] = ` +" +{ + "id": true +} +-------------- +{ + id } " `; -exports[`Query 20`] = ` +exports[`other > Query 6`] = ` " { "object": { - "$skip": {}, "id": true } } -------------- { - object @skip(if: true) { + object { id } } " `; -exports[`Query 21`] = ` +exports[`other > Query 7`] = ` " { - "object": { - "$skip": {}, - "id": true + "objectNested": { + "object": { + "string": true, + "id": true, + "int": false + } } } -------------- { - object @skip(if: true) { - id + objectNested { + object { + string + id + } } } " diff --git a/src/SelectionSet/toGraphQLDocumentString.test.ts b/src/SelectionSet/toGraphQLDocumentString.test.ts index 5abc1edc7..9ae4f8221 100644 --- a/src/SelectionSet/toGraphQLDocumentString.test.ts +++ b/src/SelectionSet/toGraphQLDocumentString.test.ts @@ -1,43 +1,73 @@ import { parse, print } from 'graphql' -import { expect, test } from 'vitest' +import { describe, expect, test } from 'vitest' import type * as Schema from '../../tests/builder/_/schema.js' import type { SelectionSet } from './__.js' import { toGraphQLDocumentString } from './toGraphQLDocumentString.js' type Q = SelectionSet.Query const s = (selectionSet: Q) => selectionSet - -test.each([ - s({ string: true }), - s({ string: 1 }), - // s({ string: false }), // todo should be static error - s({ id: true, string: false }), - s({ id: true, string: 0 }), - s({ id: true, string: undefined }), - s({ object: { id: true } }), - s({ objectNested: { object: { string: true, id: true, int: false } } }), - // $include - s({ object: { $include: true, id: true } }), - s({ object: { $include: false, id: true } }), - s({ object: { $include: undefined, id: true } }), - s({ object: { $include: { if: true }, id: true } }), - s({ object: { $include: { if: false }, id: true } }), - s({ object: { $include: { if: undefined }, id: true } }), - s({ object: { $include: {}, id: true } }), - // $skip - s({ object: { $skip: true, id: true } }), - s({ object: { $skip: false, id: true } }), - s({ object: { $skip: undefined, id: true } }), - s({ object: { $skip: { if: true }, id: true } }), - s({ object: { $skip: { if: false }, id: true } }), - s({ object: { $skip: { if: undefined }, id: true } }), - s({ object: { $skip: {}, id: true } }), -])(`Query`, (ss) => { +const prepareResult = (ss: Q) => { const graphqlDocumentString = toGraphQLDocumentString(ss) + // console.log(graphqlDocumentString) // Should parse, ensures is syntactically valid graphql document. const document = parse(graphqlDocumentString) const graphqlDocumentStringFormatted = print(document) - const beforeAfter = `\n` + JSON.stringify(ss, null, 2) - + `\n--------------\n` + graphqlDocumentStringFormatted + `\n` - expect(beforeAfter).toMatchSnapshot() + const beforeAfter = `\n` + + JSON.stringify(ss, null, 2) + + `\n--------------\n` + + graphqlDocumentStringFormatted + + `\n` + return beforeAfter +} + +describe(`args`, () => { + test.each([ + s({ stringWithArgs: { $: { boolean: true, float: 1 } } }), + // s({ stringWithArgs: { $: {} } }), + ])(`Query`, (ss) => { + expect(prepareResult(ss)).toMatchSnapshot() + }) +}) + +describe(`other`, () => { + test.each([ + s({ string: true }), + s({ string: 1 }), + // s({ string: false }), // todo should be static error + s({ id: true, string: false }), + s({ id: true, string: 0 }), + s({ id: true, string: undefined }), + s({ object: { id: true } }), + s({ objectNested: { object: { string: true, id: true, int: false } } }), + ])(`Query`, (ss) => { + expect(prepareResult(ss)).toMatchSnapshot() + }) +}) + +describe(`$include`, () => { + test.each([ + s({ object: { $include: true, id: true } }), + s({ object: { $include: false, id: true } }), + s({ object: { $include: undefined, id: true } }), + s({ object: { $include: { if: true }, id: true } }), + s({ object: { $include: { if: false }, id: true } }), + s({ object: { $include: { if: undefined }, id: true } }), + s({ object: { $include: {}, id: true } }), + ])(`Query`, (ss) => { + expect(prepareResult(ss)).toMatchSnapshot() + }) +}) + +describe(`$skip`, () => { + test.each([ + s({ object: { $skip: true, id: true } }), + s({ object: { $skip: false, id: true } }), + s({ object: { $skip: undefined, id: true } }), + s({ object: { $skip: { if: true }, id: true } }), + s({ object: { $skip: { if: false }, id: true } }), + s({ object: { $skip: { if: undefined }, id: true } }), + s({ object: { $skip: {}, id: true } }), + ])(`Query`, (ss) => { + expect(prepareResult(ss)).toMatchSnapshot() + }) }) diff --git a/src/SelectionSet/toGraphQLDocumentString.ts b/src/SelectionSet/toGraphQLDocumentString.ts index 12acda4e4..fee41e4c0 100644 --- a/src/SelectionSet/toGraphQLDocumentString.ts +++ b/src/SelectionSet/toGraphQLDocumentString.ts @@ -6,8 +6,13 @@ type SpecialFields = { // $?: Record $include?: SelectionSet.Directive.Include['$include'] $skip?: SelectionSet.Directive.Skip['$skip'] + $?: Args } +type Args = { [k: string]: Args_ } + +type Args_ = string | boolean | null | number | Args + // const specialFields = { // $include: `$include`, // $skip: `$skip`, @@ -34,8 +39,9 @@ export const toGraphQLDocumentString = (ss: {}) => { const indicatorOrSelectionSet = (ss: SelectionSet.Indicator | SS): string => { if (isIndicator(ss)) return `` - const { $include, $skip, ...rest } = ss + const { $include, $skip, $, ...rest } = ss + let args = `` let directives = `` if ($include !== undefined) { @@ -48,7 +54,19 @@ const indicatorOrSelectionSet = (ss: SelectionSet.Indicator | SS): string => { directives += `@skip(if: ${typeof $skip === `boolean` ? $skip : $skip.if === undefined ? true : $skip.if})` } - return `${directives} { + if ($ !== undefined) { + args = `(${ + Object.entries($).map(([k, v]) => { + return `${k}: ${JSON.stringify(v)}` + }).join(`, `) + })` + } + + if (Object.keys(rest).length === 0) { + return `${args} ${directives}` + } + + return `${args} ${directives} { ${selectionSet(rest)} }` } From 1c58f14fddd2aab3816f37f9eb52084e23dc89d9 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Tue, 27 Feb 2024 22:48:23 -0500 Subject: [PATCH 50/71] tests --- .../toGraphQLDocumentString.test.ts.snap | 50 +++++++++++++++++++ .../toGraphQLDocumentString.test.ts | 6 ++- src/SelectionSet/toGraphQLDocumentString.ts | 5 +- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap index 298be0a14..fa3606301 100644 --- a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap +++ b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap @@ -261,6 +261,56 @@ exports[`args > Query 1`] = ` " `; +exports[`args > Query 2`] = ` +" +{ + "stringWithArgs": { + "$": {} + } +} +-------------- +{ + stringWithArgs +} +" +`; + +exports[`args > Query 3`] = ` +" +{ + "objectWithArgs": { + "$": { + "id": "" + }, + "id": true + } +} +-------------- +{ + objectWithArgs(id: "") { + id + } +} +" +`; + +exports[`args > Query 4`] = ` +" +{ + "objectWithArgs": { + "$": {}, + "id": true + } +} +-------------- +{ + objectWithArgs { + id + } +} +" +`; + exports[`other > Query 1`] = ` " { diff --git a/src/SelectionSet/toGraphQLDocumentString.test.ts b/src/SelectionSet/toGraphQLDocumentString.test.ts index 9ae4f8221..b2b663af9 100644 --- a/src/SelectionSet/toGraphQLDocumentString.test.ts +++ b/src/SelectionSet/toGraphQLDocumentString.test.ts @@ -23,7 +23,11 @@ const prepareResult = (ss: Q) => { describe(`args`, () => { test.each([ s({ stringWithArgs: { $: { boolean: true, float: 1 } } }), - // s({ stringWithArgs: { $: {} } }), + s({ stringWithArgs: { $: {} } }), + // s({ objectWithArgs: { $: { id: `` } } }), // todo should be static error + // s({ objectWithArgs: { $: {} } }), // todo should be static error + s({ objectWithArgs: { $: { id: `` }, id: true } }), + s({ objectWithArgs: { $: {}, id: true } }), ])(`Query`, (ss) => { expect(prepareResult(ss)).toMatchSnapshot() }) diff --git a/src/SelectionSet/toGraphQLDocumentString.ts b/src/SelectionSet/toGraphQLDocumentString.ts index fee41e4c0..b531514ab 100644 --- a/src/SelectionSet/toGraphQLDocumentString.ts +++ b/src/SelectionSet/toGraphQLDocumentString.ts @@ -55,8 +55,9 @@ const indicatorOrSelectionSet = (ss: SelectionSet.Indicator | SS): string => { } if ($ !== undefined) { - args = `(${ - Object.entries($).map(([k, v]) => { + const entries = Object.entries($) + args = entries.length === 0 ? `` : `(${ + entries.map(([k, v]) => { return `${k}: ${JSON.stringify(v)}` }).join(`, `) })` From 20c6d9a839077d2477935e3ff2573de15fd3efa5 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Tue, 27 Feb 2024 23:31:16 -0500 Subject: [PATCH 51/71] aliases --- src/Schema/Schema.ts | 9 ++- src/SelectionSet/SelectionSet.ts | 6 ++ .../toGraphQLDocumentString.test.ts.snap | 57 +++++++++++++++++++ .../toGraphQLDocumentString.test.ts | 11 ++++ src/SelectionSet/toGraphQLDocumentString.ts | 11 +++- src/lib/prelude.ts | 2 + 6 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/Schema/Schema.ts b/src/Schema/Schema.ts index 77a2ea024..254047d44 100644 --- a/src/Schema/Schema.ts +++ b/src/Schema/Schema.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/ban-types */ -import type { Letter } from '../lib/prelude.js' +import type { Digit, Letter } from '../lib/prelude.js' export interface Index { Root: { @@ -47,6 +47,9 @@ export type FieldArgs = { type: object; allOptional: boolean } export type AsField = T extends Field ? T : never +/** + * @see http://spec.graphql.org/draft/#sec-Names + */ // dprint-ignore export type NameParse = T extends NameHead ? T : @@ -61,5 +64,5 @@ export type NameBodyParse = : never : never -export type NameHead = Letter -export type NameBody = Letter | '_' +export type NameHead = Letter | '_' +export type NameBody = Letter | '_' | Digit diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 19a8779d1..965f3e51c 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -178,6 +178,12 @@ export type ClientIndicator = ClientIndicatorPositive | ClientIndicatorNegative export type ClientIndicatorPositive = true | 1 export type ClientIndicatorNegative = false | 0 | undefined +/** + * @see https://regex101.com/r/XfOTMX/1 + * @see http://spec.graphql.org/draft/#sec-Names + */ +export const aliasPattern = /^(?[A-z][A-z_0-9]*)_as_(?[A-z][A-z_0-9]*)$/ + export type OmitNegativeIndicators<$SelectionSet> = { [K in keyof $SelectionSet as $SelectionSet[K] extends ClientIndicatorNegative ? never : K]: $SelectionSet[K] } diff --git a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap index fa3606301..2e7125a5f 100644 --- a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap +++ b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap @@ -244,6 +244,63 @@ exports[`$skip > Query 7`] = ` " `; +exports[`alias > Query 1`] = ` +" +{ + "id_as_x": true +} +-------------- +{ + id: x +} +" +`; + +exports[`alias > Query 2`] = ` +" +{ + "id_as_x": true, + "id_as_id2": true +} +-------------- +{ + id: x + id: id2 +} +" +`; + +exports[`alias > Query 3`] = ` +" +{ + "id_as_x": { + "$skip": true + } +} +-------------- +{ + id: x @skip(if: true) +} +" +`; + +exports[`alias > Query 4`] = ` +" +{ + "object_as_x": { + "$skip": true, + "id": true + } +} +-------------- +{ + object: x @skip(if: true) { + id + } +} +" +`; + exports[`args > Query 1`] = ` " { diff --git a/src/SelectionSet/toGraphQLDocumentString.test.ts b/src/SelectionSet/toGraphQLDocumentString.test.ts index b2b663af9..ff52d912e 100644 --- a/src/SelectionSet/toGraphQLDocumentString.test.ts +++ b/src/SelectionSet/toGraphQLDocumentString.test.ts @@ -20,6 +20,17 @@ const prepareResult = (ss: Q) => { return beforeAfter } +describe(`alias`, () => { + test.each([ + s({ id_as_x: true }), + s({ id_as_x: true, id_as_id2: true }), + s({ id_as_x: { $skip: true } }), + s({ object_as_x: { $skip: true, id: true } }), + ])(`Query`, (ss) => { + expect(prepareResult(ss)).toMatchSnapshot() + }) +}) + describe(`args`, () => { test.each([ s({ stringWithArgs: { $: { boolean: true, float: 1 } } }), diff --git a/src/SelectionSet/toGraphQLDocumentString.ts b/src/SelectionSet/toGraphQLDocumentString.ts index b531514ab..bd1826e00 100644 --- a/src/SelectionSet/toGraphQLDocumentString.ts +++ b/src/SelectionSet/toGraphQLDocumentString.ts @@ -1,4 +1,5 @@ import type { SelectionSet } from './__.js' +import { aliasPattern } from './SelectionSet.js' type SpecialFields = { // todo @@ -76,10 +77,18 @@ const selectionSet = (ss: SS) => { return Object.entries(ss).filter(([k, v]) => { return isPositiveIndicator(v) }).map(([field, ss]) => { - return `${field} ${indicatorOrSelectionSet(ss)}` + return `${resolveAlias(field)} ${indicatorOrSelectionSet(ss)}` }).join(`\n`) + `\n` } +const resolveAlias = (field: string) => { + const match = field.match(aliasPattern) + if (match?.groups) { + return `${match.groups[`actual`]}: ${match.groups[`alias`]}` + } + return field +} + const isIndicator = (v: any): v is SelectionSet.Indicator => { return String(v) in indicator } diff --git a/src/lib/prelude.ts b/src/lib/prelude.ts index 7f1790603..e62c811bd 100644 --- a/src/lib/prelude.ts +++ b/src/lib/prelude.ts @@ -96,6 +96,8 @@ export type Narrowable = string | number | bigint | boolean | [] export type Letter = LetterLower | LetterUpper +export type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' + export type LetterLower = | 'a' | 'b' From 82b8a0a7dd957a9ac22290714390ae3bf3ac18a4 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Tue, 27 Feb 2024 23:40:26 -0500 Subject: [PATCH 52/71] some types --- .../toGraphQLDocumentString.test.ts | 2 +- src/SelectionSet/toGraphQLDocumentString.ts | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/SelectionSet/toGraphQLDocumentString.test.ts b/src/SelectionSet/toGraphQLDocumentString.test.ts index ff52d912e..8a2b3eb77 100644 --- a/src/SelectionSet/toGraphQLDocumentString.test.ts +++ b/src/SelectionSet/toGraphQLDocumentString.test.ts @@ -7,7 +7,7 @@ import { toGraphQLDocumentString } from './toGraphQLDocumentString.js' type Q = SelectionSet.Query const s = (selectionSet: Q) => selectionSet const prepareResult = (ss: Q) => { - const graphqlDocumentString = toGraphQLDocumentString(ss) + const graphqlDocumentString = toGraphQLDocumentString(ss as any) // console.log(graphqlDocumentString) // Should parse, ensures is syntactically valid graphql document. const document = parse(graphqlDocumentString) diff --git a/src/SelectionSet/toGraphQLDocumentString.ts b/src/SelectionSet/toGraphQLDocumentString.ts index bd1826e00..bd178ff77 100644 --- a/src/SelectionSet/toGraphQLDocumentString.ts +++ b/src/SelectionSet/toGraphQLDocumentString.ts @@ -20,16 +20,17 @@ type Args_ = string | boolean | null | number | Args // $scalars: `$scalars`, // $: `$`, // } +type Indicator = 0 | 1 | boolean -type SS = { - [k: string]: SS_ -} & SpecialFields +type RootSS = { + [k: string]: Indicator | SS +} -type SS_ = { - [k: string]: SelectionSet.Indicator | SS_ +type SS = { + [k: string]: Indicator | SS } & SpecialFields -export const toGraphQLDocumentString = (ss: {}) => { +export const toGraphQLDocumentString = (ss: RootSS) => { let docString = `` docString += `query { ${selectionSet(ss)} @@ -37,7 +38,7 @@ export const toGraphQLDocumentString = (ss: {}) => { return docString } -const indicatorOrSelectionSet = (ss: SelectionSet.Indicator | SS): string => { +const indicatorOrSelectionSet = (ss: Indicator | SS): string => { if (isIndicator(ss)) return `` const { $include, $skip, $, ...rest } = ss @@ -73,8 +74,8 @@ const indicatorOrSelectionSet = (ss: SelectionSet.Indicator | SS): string => { }` } -const selectionSet = (ss: SS) => { - return Object.entries(ss).filter(([k, v]) => { +const selectionSet = (ss: RootSS) => { + return Object.entries(ss).filter(([_, v]) => { return isPositiveIndicator(v) }).map(([field, ss]) => { return `${resolveAlias(field)} ${indicatorOrSelectionSet(ss)}` From ef9ad231b5c869b864ae2b98f918d7c2b366bca8 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Tue, 27 Feb 2024 23:47:43 -0500 Subject: [PATCH 53/71] union fragment --- .../toGraphQLDocumentString.test.ts.snap | 81 +++++++++++++++++-- .../toGraphQLDocumentString.test.ts | 42 ++++++---- src/SelectionSet/toGraphQLDocumentString.ts | 17 +++- 3 files changed, 115 insertions(+), 25 deletions(-) diff --git a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap index 2e7125a5f..bec1a7570 100644 --- a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap +++ b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap @@ -370,6 +370,18 @@ exports[`args > Query 4`] = ` exports[`other > Query 1`] = ` " +{ + "__typename": true +} +-------------- +{ + __typename +} +" +`; + +exports[`other > Query 2`] = ` +" { "string": true } @@ -380,7 +392,7 @@ exports[`other > Query 1`] = ` " `; -exports[`other > Query 2`] = ` +exports[`other > Query 3`] = ` " { "string": 1 @@ -392,7 +404,7 @@ exports[`other > Query 2`] = ` " `; -exports[`other > Query 3`] = ` +exports[`other > Query 4`] = ` " { "id": true, @@ -405,7 +417,7 @@ exports[`other > Query 3`] = ` " `; -exports[`other > Query 4`] = ` +exports[`other > Query 5`] = ` " { "id": true, @@ -418,7 +430,7 @@ exports[`other > Query 4`] = ` " `; -exports[`other > Query 5`] = ` +exports[`other > Query 6`] = ` " { "id": true @@ -430,7 +442,7 @@ exports[`other > Query 5`] = ` " `; -exports[`other > Query 6`] = ` +exports[`other > Query 7`] = ` " { "object": { @@ -446,7 +458,7 @@ exports[`other > Query 6`] = ` " `; -exports[`other > Query 7`] = ` +exports[`other > Query 8`] = ` " { "objectNested": { @@ -468,3 +480,60 @@ exports[`other > Query 7`] = ` } " `; + +exports[`union > Query 1`] = ` +" +{ + "fooBarUnion": { + "__typename": true + } +} +-------------- +{ + fooBarUnion { + __typename + } +} +" +`; + +exports[`union > Query 2`] = ` +" +{ + "fooBarUnion": { + "onBar": { + "int": true + } + } +} +-------------- +{ + fooBarUnion { + ... on Bar { + int + } + } +} +" +`; + +exports[`union > Query 3`] = ` +" +{ + "fooBarUnion": { + "onBar": { + "$skip": true, + "int": true + } + } +} +-------------- +{ + fooBarUnion { + ... on Bar @skip(if: true) { + int + } + } +} +" +`; diff --git a/src/SelectionSet/toGraphQLDocumentString.test.ts b/src/SelectionSet/toGraphQLDocumentString.test.ts index 8a2b3eb77..a1a6ab70c 100644 --- a/src/SelectionSet/toGraphQLDocumentString.test.ts +++ b/src/SelectionSet/toGraphQLDocumentString.test.ts @@ -20,6 +20,17 @@ const prepareResult = (ss: Q) => { return beforeAfter } +describe(`union`, () => { + test.each([ + s({ fooBarUnion: { __typename: true } }), + s({ fooBarUnion: { onBar: { int: true } } }), + s({ fooBarUnion: { onBar: { $skip: true, int: true } } }), + // s({ fooBarUnion: { onBar: {} } }), // todo should be static type error + ])(`Query`, (ss) => { + expect(prepareResult(ss)).toMatchSnapshot() + }) +}) + describe(`alias`, () => { test.each([ s({ id_as_x: true }), @@ -44,21 +55,6 @@ describe(`args`, () => { }) }) -describe(`other`, () => { - test.each([ - s({ string: true }), - s({ string: 1 }), - // s({ string: false }), // todo should be static error - s({ id: true, string: false }), - s({ id: true, string: 0 }), - s({ id: true, string: undefined }), - s({ object: { id: true } }), - s({ objectNested: { object: { string: true, id: true, int: false } } }), - ])(`Query`, (ss) => { - expect(prepareResult(ss)).toMatchSnapshot() - }) -}) - describe(`$include`, () => { test.each([ s({ object: { $include: true, id: true } }), @@ -86,3 +82,19 @@ describe(`$skip`, () => { expect(prepareResult(ss)).toMatchSnapshot() }) }) + +describe(`other`, () => { + test.each([ + s({ __typename: true }), + s({ string: true }), + s({ string: 1 }), + // s({ string: false }), // todo should be static error + s({ id: true, string: false }), + s({ id: true, string: 0 }), + s({ id: true, string: undefined }), + s({ object: { id: true } }), + s({ objectNested: { object: { string: true, id: true, int: false } } }), + ])(`Query`, (ss) => { + expect(prepareResult(ss)).toMatchSnapshot() + }) +}) diff --git a/src/SelectionSet/toGraphQLDocumentString.ts b/src/SelectionSet/toGraphQLDocumentString.ts index bd178ff77..142b42e01 100644 --- a/src/SelectionSet/toGraphQLDocumentString.ts +++ b/src/SelectionSet/toGraphQLDocumentString.ts @@ -22,7 +22,7 @@ type Args_ = string | boolean | null | number | Args // } type Indicator = 0 | 1 | boolean -type RootSS = { +type SSRoot = { [k: string]: Indicator | SS } @@ -30,7 +30,7 @@ type SS = { [k: string]: Indicator | SS } & SpecialFields -export const toGraphQLDocumentString = (ss: RootSS) => { +export const toGraphQLDocumentString = (ss: SSRoot) => { let docString = `` docString += `query { ${selectionSet(ss)} @@ -74,14 +74,23 @@ const indicatorOrSelectionSet = (ss: Indicator | SS): string => { }` } -const selectionSet = (ss: RootSS) => { +const selectionSet = (ss: SSRoot) => { return Object.entries(ss).filter(([_, v]) => { return isPositiveIndicator(v) }).map(([field, ss]) => { - return `${resolveAlias(field)} ${indicatorOrSelectionSet(ss)}` + return `${resolveFragment(resolveAlias(field))} ${indicatorOrSelectionSet(ss)}` }).join(`\n`) + `\n` } +const fragmentPattern = /^on(?[A-Z][A-z_0-9]*)$/ + +const resolveFragment = (field: string) => { + const match = field.match(fragmentPattern) + if (match?.groups) { + return `...on ${match.groups[`name`]}` + } + return field +} const resolveAlias = (field: string) => { const match = field.match(aliasPattern) if (match?.groups) { From 330fe3448d007d56d5d1c8a0901fc01e15cf3568 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Wed, 28 Feb 2024 00:09:03 -0500 Subject: [PATCH 54/71] defer stream --- src/SelectionSet/SelectionSet.test-d.ts | 2 +- src/SelectionSet/SelectionSet.ts | 11 + .../toGraphQLDocumentString.test.ts.snap | 327 ++++++++++++++++++ .../toGraphQLDocumentString.test.ts | 32 ++ src/SelectionSet/toGraphQLDocumentString.ts | 36 +- 5 files changed, 402 insertions(+), 6 deletions(-) diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 1e1b4bb9e..46fcf9c6d 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -10,7 +10,7 @@ test(`ParseAliasExpression`, () => { expectTypeOf>().toEqualTypeOf<'$'>() expectTypeOf>().toEqualTypeOf<'a_as_$'>() expectTypeOf>().toEqualTypeOf<'$_as_b'>() - expectTypeOf>().toEqualTypeOf<'__as__'>() + expectTypeOf>().toEqualTypeOf<'1_as_2'>() }) test(`Query`, () => { diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 965f3e51c..672c735f6 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -165,6 +165,16 @@ export namespace Directive { export type Positive = { $skip: true | { if: true } } export type Negative = { $skip: false | { if: false } } } + export type Defer = { $defer: boolean | { if?: boolean; label?: string } } + export namespace Defer { + export type Positive = { $defer: true | { if: true } } + export type Negative = { $defer: false | { if: false } } + } + export type Stream = { $stream: boolean | { if?: boolean; label?: string; initialCount?: number } } + export namespace Stream { + export type Positive = { $stream: true | { if: true } } + export type Negative = { $stream: false | { if: false } } + } } /** @@ -183,6 +193,7 @@ export type ClientIndicatorNegative = false | 0 | undefined * @see http://spec.graphql.org/draft/#sec-Names */ export const aliasPattern = /^(?[A-z][A-z_0-9]*)_as_(?[A-z][A-z_0-9]*)$/ +export const fragmentPattern = /^on(?[A-Z][A-z_0-9]*)$/ export type OmitNegativeIndicators<$SelectionSet> = { [K in keyof $SelectionSet as $SelectionSet[K] extends ClientIndicatorNegative ? never : K]: $SelectionSet[K] diff --git a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap index bec1a7570..0ea0838af 100644 --- a/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap +++ b/src/SelectionSet/__snapshots__/toGraphQLDocumentString.test.ts.snap @@ -1,5 +1,146 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`$defer > Query 1`] = ` +" +{ + "object": { + "$defer": true, + "id": true + } +} +-------------- +{ + object @defer(if: true) { + id + } +} +" +`; + +exports[`$defer > Query 2`] = ` +" +{ + "object": { + "$defer": false, + "id": true + } +} +-------------- +{ + object @defer(if: false) { + id + } +} +" +`; + +exports[`$defer > Query 3`] = ` +" +{ + "object": { + "id": true + } +} +-------------- +{ + object { + id + } +} +" +`; + +exports[`$defer > Query 4`] = ` +" +{ + "object": { + "$defer": { + "if": true + }, + "id": true + } +} +-------------- +{ + object @defer(if: true) { + id + } +} +" +`; + +exports[`$defer > Query 5`] = ` +" +{ + "object": { + "$defer": { + "if": false + }, + "id": true + } +} +-------------- +{ + object @defer(if: false) { + id + } +} +" +`; + +exports[`$defer > Query 6`] = ` +" +{ + "object": { + "$defer": {}, + "id": true + } +} +-------------- +{ + object @defer(if: true) { + id + } +} +" +`; + +exports[`$defer > Query 7`] = ` +" +{ + "object": { + "$defer": {}, + "id": true + } +} +-------------- +{ + object @defer(if: true) { + id + } +} +" +`; + +exports[`$defer > Query 8`] = ` +" +{ + "object": { + "$defer": { + "label": "foobar" + }, + "id": true + } +} +-------------- +{ + object @defer(if: true, label: "foobar") { + id + } +} +" +`; + exports[`$include > Query 1`] = ` " { @@ -244,6 +385,166 @@ exports[`$skip > Query 7`] = ` " `; +exports[`$stream > Query 1`] = ` +" +{ + "object": { + "$stream": true, + "id": true + } +} +-------------- +{ + object @defer(if: true) { + id + } +} +" +`; + +exports[`$stream > Query 2`] = ` +" +{ + "object": { + "$stream": false, + "id": true + } +} +-------------- +{ + object @defer(if: false) { + id + } +} +" +`; + +exports[`$stream > Query 3`] = ` +" +{ + "object": { + "id": true + } +} +-------------- +{ + object { + id + } +} +" +`; + +exports[`$stream > Query 4`] = ` +" +{ + "object": { + "$stream": { + "if": true + }, + "id": true + } +} +-------------- +{ + object @defer(if: true) { + id + } +} +" +`; + +exports[`$stream > Query 5`] = ` +" +{ + "object": { + "$stream": { + "if": false + }, + "id": true + } +} +-------------- +{ + object @defer(if: false) { + id + } +} +" +`; + +exports[`$stream > Query 6`] = ` +" +{ + "object": { + "$stream": {}, + "id": true + } +} +-------------- +{ + object @defer(if: true) { + id + } +} +" +`; + +exports[`$stream > Query 7`] = ` +" +{ + "object": { + "$stream": {}, + "id": true + } +} +-------------- +{ + object @defer(if: true) { + id + } +} +" +`; + +exports[`$stream > Query 8`] = ` +" +{ + "object": { + "$stream": { + "label": "foobar" + }, + "id": true + } +} +-------------- +{ + object @defer(if: true, label: "foobar") { + id + } +} +" +`; + +exports[`$stream > Query 9`] = ` +" +{ + "object": { + "$stream": { + "initialCount": 5 + }, + "id": true + } +} +-------------- +{ + object @defer(if: true, initialCount: 5) { + id + } +} +" +`; + exports[`alias > Query 1`] = ` " { @@ -481,6 +782,32 @@ exports[`other > Query 8`] = ` " `; +exports[`other > Query 9`] = ` +" +{ + "objectNested": { + "object": { + "string": true, + "id": true, + "int": { + "$skip": true + } + } + } +} +-------------- +{ + objectNested { + object { + string + id + int @skip(if: true) + } + } +} +" +`; + exports[`union > Query 1`] = ` " { diff --git a/src/SelectionSet/toGraphQLDocumentString.test.ts b/src/SelectionSet/toGraphQLDocumentString.test.ts index a1a6ab70c..aa06996e2 100644 --- a/src/SelectionSet/toGraphQLDocumentString.test.ts +++ b/src/SelectionSet/toGraphQLDocumentString.test.ts @@ -83,6 +83,37 @@ describe(`$skip`, () => { }) }) +describe(`$defer`, () => { + test.each([ + s({ object: { $defer: true, id: true } }), + s({ object: { $defer: false, id: true } }), + s({ object: { $defer: undefined, id: true } }), + s({ object: { $defer: { if: true }, id: true } }), + s({ object: { $defer: { if: false }, id: true } }), + s({ object: { $defer: { if: undefined }, id: true } }), + s({ object: { $defer: {}, id: true } }), + s({ object: { $defer: { label: `foobar` }, id: true } }), + ])(`Query`, (ss) => { + expect(prepareResult(ss)).toMatchSnapshot() + }) +}) + +describe(`$stream`, () => { + test.each([ + s({ object: { $stream: true, id: true } }), + s({ object: { $stream: false, id: true } }), + s({ object: { $stream: undefined, id: true } }), + s({ object: { $stream: { if: true }, id: true } }), + s({ object: { $stream: { if: false }, id: true } }), + s({ object: { $stream: { if: undefined }, id: true } }), + s({ object: { $stream: {}, id: true } }), + s({ object: { $stream: { label: `foobar` }, id: true } }), + s({ object: { $stream: { initialCount: 5 }, id: true } }), + ])(`Query`, (ss) => { + expect(prepareResult(ss)).toMatchSnapshot() + }) +}) + describe(`other`, () => { test.each([ s({ __typename: true }), @@ -94,6 +125,7 @@ describe(`other`, () => { s({ id: true, string: undefined }), s({ object: { id: true } }), s({ objectNested: { object: { string: true, id: true, int: false } } }), + s({ objectNested: { object: { string: true, id: true, int: { $skip: true } } } }), ])(`Query`, (ss) => { expect(prepareResult(ss)).toMatchSnapshot() }) diff --git a/src/SelectionSet/toGraphQLDocumentString.ts b/src/SelectionSet/toGraphQLDocumentString.ts index 142b42e01..9d4c21e2a 100644 --- a/src/SelectionSet/toGraphQLDocumentString.ts +++ b/src/SelectionSet/toGraphQLDocumentString.ts @@ -1,5 +1,5 @@ import type { SelectionSet } from './__.js' -import { aliasPattern } from './SelectionSet.js' +import { aliasPattern, fragmentPattern } from './SelectionSet.js' type SpecialFields = { // todo @@ -7,6 +7,8 @@ type SpecialFields = { // $?: Record $include?: SelectionSet.Directive.Include['$include'] $skip?: SelectionSet.Directive.Skip['$skip'] + $defer?: SelectionSet.Directive.Defer['$defer'] + $stream?: SelectionSet.Directive.Stream['$stream'] $?: Args } @@ -38,14 +40,37 @@ export const toGraphQLDocumentString = (ss: SSRoot) => { return docString } +const directiveArgs = (config: object) => { + return Object.entries(config).filter(([_, v]) => v !== undefined).map(([k, v]) => { + return `${k}: ${JSON.stringify(v)}` + }).join(`, `) +} + const indicatorOrSelectionSet = (ss: Indicator | SS): string => { if (isIndicator(ss)) return `` - const { $include, $skip, $, ...rest } = ss + const { $include, $skip, $defer, $stream, $, ...rest } = ss let args = `` let directives = `` + if ($stream !== undefined) { + const config = { + if: typeof $stream === `boolean` ? $stream : $stream.if === undefined ? true : $stream.if, + label: typeof $stream === `boolean` ? undefined : $stream.label, + initialCount: typeof $stream === `boolean` ? undefined : $stream.initialCount, + } + directives += `@defer(${directiveArgs(config)})` + } + + if ($defer !== undefined) { + const config = { + if: typeof $defer === `boolean` ? $defer : $defer.if === undefined ? true : $defer.if, + label: typeof $defer === `boolean` ? undefined : $defer.label, + } + directives += `@defer(${directiveArgs(config)})` + } + if ($include !== undefined) { directives += `@include(if: ${ typeof $include === `boolean` ? $include : $include.if === undefined ? true : $include.if @@ -82,8 +107,7 @@ const selectionSet = (ss: SSRoot) => { }).join(`\n`) + `\n` } -const fragmentPattern = /^on(?[A-Z][A-z_0-9]*)$/ - +// todo use a given schema to ensure that field is actually a fragment and not just happened to be using pattern onX const resolveFragment = (field: string) => { const match = field.match(fragmentPattern) if (match?.groups) { @@ -91,6 +115,8 @@ const resolveFragment = (field: string) => { } return field } + +// todo use a given schema to ensure that field is actually a fragment and not just happened to be using pattern onX const resolveAlias = (field: string) => { const match = field.match(aliasPattern) if (match?.groups) { @@ -99,7 +125,7 @@ const resolveAlias = (field: string) => { return field } -const isIndicator = (v: any): v is SelectionSet.Indicator => { +const isIndicator = (v: any): v is Indicator => { return String(v) in indicator } From bbad8a6b262afa9613bc35059e88570197aa855a Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Wed, 28 Feb 2024 00:10:14 -0500 Subject: [PATCH 55/71] last todo --- src/SelectionSet/toGraphQLDocumentString.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/SelectionSet/toGraphQLDocumentString.ts b/src/SelectionSet/toGraphQLDocumentString.ts index 9d4c21e2a..efc8067e9 100644 --- a/src/SelectionSet/toGraphQLDocumentString.ts +++ b/src/SelectionSet/toGraphQLDocumentString.ts @@ -2,9 +2,8 @@ import type { SelectionSet } from './__.js' import { aliasPattern, fragmentPattern } from './SelectionSet.js' type SpecialFields = { - // todo + // todo - this requires having the schema at runtime to know which fields to select. // $scalars?: SelectionSet.Indicator - // $?: Record $include?: SelectionSet.Directive.Include['$include'] $skip?: SelectionSet.Directive.Skip['$skip'] $defer?: SelectionSet.Directive.Defer['$defer'] @@ -16,12 +15,6 @@ type Args = { [k: string]: Args_ } type Args_ = string | boolean | null | number | Args -// const specialFields = { -// $include: `$include`, -// $skip: `$skip`, -// $scalars: `$scalars`, -// $: `$`, -// } type Indicator = 0 | 1 | boolean type SSRoot = { From 8c4519e09105a40bf60004c8c458ae4ce41c7b6b Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Wed, 28 Feb 2024 00:11:56 -0500 Subject: [PATCH 56/71] fix --- src/Schema/Schema.test-d.ts | 5 +++-- src/client.ts | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Schema/Schema.test-d.ts b/src/Schema/Schema.test-d.ts index b9c6adbfc..ed2c2a83a 100644 --- a/src/Schema/Schema.test-d.ts +++ b/src/Schema/Schema.test-d.ts @@ -3,14 +3,15 @@ import type { Schema } from './__.js' test(`NameParse`, () => { expectTypeOf>().toEqualTypeOf<'a'>() + expectTypeOf>().toEqualTypeOf<'a1'>() expectTypeOf>().toEqualTypeOf<'A'>() expectTypeOf>().toEqualTypeOf<'aa'>() expectTypeOf>().toEqualTypeOf<'a_'>() expectTypeOf>().toEqualTypeOf<'a__'>() expectTypeOf>().toEqualTypeOf<'a__b'>() expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() }) diff --git a/src/client.ts b/src/client.ts index 73cb75b69..6f09d4a45 100644 --- a/src/client.ts +++ b/src/client.ts @@ -22,4 +22,5 @@ interface Input { } export const create = <$SchemaIndex extends Index>(input: Input): Client<$SchemaIndex> => { + return input as any } From cccd996f3d96d4b7b1804ed4f83bc269d218fd1b Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Wed, 28 Feb 2024 00:12:15 -0500 Subject: [PATCH 57/71] format --- tests/builder/_/schema.ts | 1018 +++++++++++++++++++------------------ 1 file changed, 510 insertions(+), 508 deletions(-) diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index a9a2c357f..f70ae1226 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -1,34 +1,35 @@ export namespace $ { -export interface Index { -Root: { -Query: Root.Query -Mutation: null -Subscription: null -} -objects: { -Foo: Object.Foo -Bar: Object.Bar -ObjectNested: Object.ObjectNested -Object: Object.Object -Object1ImplementingInterface: Object.Object1ImplementingInterface -Object2ImplementingInterface: Object.Object2ImplementingInterface -} -unionMemberNames: { -FooBarUnion: "Foo" -| "Bar" -} -unions: { -Union: Union.FooBarUnion -} -scalars: Scalars -} -export interface Scalars { -ID: string -String: string -Int: number -Float: number -Boolean: boolean -} + export interface Index { + Root: { + Query: Root.Query + Mutation: null + Subscription: null + } + objects: { + Foo: Object.Foo + Bar: Object.Bar + ObjectNested: Object.ObjectNested + Object: Object.Object + Object1ImplementingInterface: Object.Object1ImplementingInterface + Object2ImplementingInterface: Object.Object2ImplementingInterface + } + unionMemberNames: { + FooBarUnion: + | 'Foo' + | 'Bar' + } + unions: { + Union: Union.FooBarUnion + } + scalars: Scalars + } + export interface Scalars { + ID: string + String: string + Int: number + Float: number + Boolean: boolean + } } // ------------------------------------------------------------ // @@ -36,233 +37,233 @@ Boolean: boolean // ------------------------------------------------------------ // export namespace Root { -export interface Query { -__typename: { -type: { -kind: "literal" -value: "Query" -} -args: null -namedType: "Query" -} -interface: { -type: { -kind: "nullable" -type: { -kind: "named" -named: Interface.Interface -} -} -namedType: Interface.Interface -args: null -} -id: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["ID"] -} -} -namedType: $.Scalars["ID"] -args: null -} -idNonNull: { -type: { -kind: "named" -named: $.Scalars["ID"] -} -namedType: $.Scalars["ID"] -args: null -} -string: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["String"] -} -} -namedType: $.Scalars["String"] -args: null -} -stringWithRequiredArg: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["String"] -} -} -namedType: $.Scalars["String"] -args: { -type: { -string: $.Scalars["String"] -} -allOptional: false -} -} -stringWithArgs: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["String"] -} -} -namedType: $.Scalars["String"] -args: { -type: { -string?: $.Scalars["String"] | null -int?: $.Scalars["Int"] | null -float?: $.Scalars["Float"] | null -boolean?: $.Scalars["Boolean"] | null -id?: $.Scalars["ID"] | null -} -allOptional: true -} -} -object: { -type: { -kind: "nullable" -type: { -kind: "named" -named: Object.Object -} -} -namedType: Object.Object -args: null -} -listListIntNonNull: { -type: { -kind: "list" -type: { -kind: "list" -type: { -kind: "named" -named: $.Scalars["Int"] -} -} -} -namedType: $.Scalars["Int"] -args: null -} -listListInt: { -type: { -kind: "nullable" -type: { -kind: "list" -type: { -kind: "nullable" -type: { -kind: "list" -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["Int"] -} -} -} -} -} -} -namedType: $.Scalars["Int"] -args: null -} -listInt: { -type: { -kind: "nullable" -type: { -kind: "list" -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["Int"] -} -} -} -} -namedType: $.Scalars["Int"] -args: null -} -listIntNonNull: { -type: { -kind: "list" -type: { -kind: "named" -named: $.Scalars["Int"] -} -} -namedType: $.Scalars["Int"] -args: null -} -objectNested: { -type: { -kind: "nullable" -type: { -kind: "named" -named: Object.ObjectNested -} -} -namedType: Object.ObjectNested -args: null -} -objectNonNull: { -type: { -kind: "named" -named: Object.Object -} -namedType: Object.Object -args: null -} -objectWithArgs: { -type: { -kind: "nullable" -type: { -kind: "named" -named: Object.Object -} -} -namedType: Object.Object -args: { -type: { -string?: $.Scalars["String"] | null -int?: $.Scalars["Int"] | null -float?: $.Scalars["Float"] | null -boolean?: $.Scalars["Boolean"] | null -id?: $.Scalars["ID"] | null -} -allOptional: true -} -} -fooBarUnion: { -type: { -kind: "nullable" -type: { -kind: "named" -named: Union.FooBarUnion -} -} -namedType: Union.FooBarUnion -args: null -} -/** -* Query enum field documentation. -*/ -abcEnum: { -type: { -kind: "nullable" -type: { -kind: "named" -named: Enum.ABCEnum -} -} -namedType: Enum.ABCEnum -args: null -} -} + export interface Query { + __typename: { + type: { + kind: 'literal' + value: 'Query' + } + args: null + namedType: 'Query' + } + interface: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: Interface.Interface + } + } + namedType: Interface.Interface + args: null + } + id: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['ID'] + } + } + namedType: $.Scalars['ID'] + args: null + } + idNonNull: { + type: { + kind: 'named' + named: $.Scalars['ID'] + } + namedType: $.Scalars['ID'] + args: null + } + string: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['String'] + } + } + namedType: $.Scalars['String'] + args: null + } + stringWithRequiredArg: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['String'] + } + } + namedType: $.Scalars['String'] + args: { + type: { + string: $.Scalars['String'] + } + allOptional: false + } + } + stringWithArgs: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['String'] + } + } + namedType: $.Scalars['String'] + args: { + type: { + string?: $.Scalars['String'] | null + int?: $.Scalars['Int'] | null + float?: $.Scalars['Float'] | null + boolean?: $.Scalars['Boolean'] | null + id?: $.Scalars['ID'] | null + } + allOptional: true + } + } + object: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: Object.Object + } + } + namedType: Object.Object + args: null + } + listListIntNonNull: { + type: { + kind: 'list' + type: { + kind: 'list' + type: { + kind: 'named' + named: $.Scalars['Int'] + } + } + } + namedType: $.Scalars['Int'] + args: null + } + listListInt: { + type: { + kind: 'nullable' + type: { + kind: 'list' + type: { + kind: 'nullable' + type: { + kind: 'list' + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['Int'] + } + } + } + } + } + } + namedType: $.Scalars['Int'] + args: null + } + listInt: { + type: { + kind: 'nullable' + type: { + kind: 'list' + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['Int'] + } + } + } + } + namedType: $.Scalars['Int'] + args: null + } + listIntNonNull: { + type: { + kind: 'list' + type: { + kind: 'named' + named: $.Scalars['Int'] + } + } + namedType: $.Scalars['Int'] + args: null + } + objectNested: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: Object.ObjectNested + } + } + namedType: Object.ObjectNested + args: null + } + objectNonNull: { + type: { + kind: 'named' + named: Object.Object + } + namedType: Object.Object + args: null + } + objectWithArgs: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: Object.Object + } + } + namedType: Object.Object + args: { + type: { + string?: $.Scalars['String'] | null + int?: $.Scalars['Int'] | null + float?: $.Scalars['Float'] | null + boolean?: $.Scalars['Boolean'] | null + id?: $.Scalars['ID'] | null + } + allOptional: true + } + } + fooBarUnion: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: Union.FooBarUnion + } + } + namedType: Union.FooBarUnion + args: null + } + /** + * Query enum field documentation. + */ + abcEnum: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: Enum.ABCEnum + } + } + namedType: Enum.ABCEnum + args: null + } + } } // ------------------------------------------------------------ // @@ -270,18 +271,18 @@ args: null // ------------------------------------------------------------ // export namespace Enum { -/** -* Enum documentation. -* -* Members -* "A" - (DEPRECATED: Enum value A is deprecated.) -* "B" - Enum B member documentation. -* "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) -*/ -export type ABCEnum = -| "A" -| "B" -| "C" + /** + * Enum documentation. + * + * Members + * "A" - (DEPRECATED: Enum value A is deprecated.) + * "B" - Enum B member documentation. + * "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) + */ + export type ABCEnum = + | 'A' + | 'B' + | 'C' } // ------------------------------------------------------------ // @@ -289,8 +290,7 @@ export type ABCEnum = // ------------------------------------------------------------ // export namespace InputObject { -// -- no types -- - + // -- no types -- } // ------------------------------------------------------------ // @@ -298,24 +298,25 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { -export interface Interface { -__interfacename: "Interface" -type: { -id: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["ID"] -} -} -namedType: $.Scalars["ID"] -args: null -} -} -implementors: Object.Object1ImplementingInterface -| Object.Object2ImplementingInterface -} + export interface Interface { + __interfacename: 'Interface' + type: { + id: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['ID'] + } + } + namedType: $.Scalars['ID'] + args: null + } + } + implementors: + | Object.Object1ImplementingInterface + | Object.Object2ImplementingInterface + } } // ------------------------------------------------------------ // @@ -323,222 +324,222 @@ implementors: Object.Object1ImplementingInterface // ------------------------------------------------------------ // export namespace Object { -/** -* Object documentation. -*/ -export interface Foo { -__typename: { -type: { -kind: "literal" -value: "Foo" -} -args: null -namedType: "Foo" -} -/** -* Field documentation. -* -* @deprecated Field a is deprecated. -*/ -id: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["ID"] -} -} -namedType: $.Scalars["ID"] -args: null -} -} + /** + * Object documentation. + */ + export interface Foo { + __typename: { + type: { + kind: 'literal' + value: 'Foo' + } + args: null + namedType: 'Foo' + } + /** + * Field documentation. + * + * @deprecated Field a is deprecated. + */ + id: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['ID'] + } + } + namedType: $.Scalars['ID'] + args: null + } + } -export interface Bar { -__typename: { -type: { -kind: "literal" -value: "Bar" -} -args: null -namedType: "Bar" -} -int: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["Int"] -} -} -namedType: $.Scalars["Int"] -args: null -} -} + export interface Bar { + __typename: { + type: { + kind: 'literal' + value: 'Bar' + } + args: null + namedType: 'Bar' + } + int: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['Int'] + } + } + namedType: $.Scalars['Int'] + args: null + } + } -export interface ObjectNested { -__typename: { -type: { -kind: "literal" -value: "ObjectNested" -} -args: null -namedType: "ObjectNested" -} -id: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["ID"] -} -} -namedType: $.Scalars["ID"] -args: null -} -object: { -type: { -kind: "nullable" -type: { -kind: "named" -named: Object.Object -} -} -namedType: Object.Object -args: null -} -} + export interface ObjectNested { + __typename: { + type: { + kind: 'literal' + value: 'ObjectNested' + } + args: null + namedType: 'ObjectNested' + } + id: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['ID'] + } + } + namedType: $.Scalars['ID'] + args: null + } + object: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: Object.Object + } + } + namedType: Object.Object + args: null + } + } -export interface Object { -__typename: { -type: { -kind: "literal" -value: "Object" -} -args: null -namedType: "Object" -} -string: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["String"] -} -} -namedType: $.Scalars["String"] -args: null -} -int: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["Int"] -} -} -namedType: $.Scalars["Int"] -args: null -} -float: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["Float"] -} -} -namedType: $.Scalars["Float"] -args: null -} -boolean: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["Boolean"] -} -} -namedType: $.Scalars["Boolean"] -args: null -} -id: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["ID"] -} -} -namedType: $.Scalars["ID"] -args: null -} -} + export interface Object { + __typename: { + type: { + kind: 'literal' + value: 'Object' + } + args: null + namedType: 'Object' + } + string: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['String'] + } + } + namedType: $.Scalars['String'] + args: null + } + int: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['Int'] + } + } + namedType: $.Scalars['Int'] + args: null + } + float: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['Float'] + } + } + namedType: $.Scalars['Float'] + args: null + } + boolean: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['Boolean'] + } + } + namedType: $.Scalars['Boolean'] + args: null + } + id: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['ID'] + } + } + namedType: $.Scalars['ID'] + args: null + } + } -export interface Object1ImplementingInterface { -__typename: { -type: { -kind: "literal" -value: "Object1ImplementingInterface" -} -args: null -namedType: "Object1ImplementingInterface" -} -id: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["ID"] -} -} -namedType: $.Scalars["ID"] -args: null -} -int: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["Int"] -} -} -namedType: $.Scalars["Int"] -args: null -} -} + export interface Object1ImplementingInterface { + __typename: { + type: { + kind: 'literal' + value: 'Object1ImplementingInterface' + } + args: null + namedType: 'Object1ImplementingInterface' + } + id: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['ID'] + } + } + namedType: $.Scalars['ID'] + args: null + } + int: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['Int'] + } + } + namedType: $.Scalars['Int'] + args: null + } + } -export interface Object2ImplementingInterface { -__typename: { -type: { -kind: "literal" -value: "Object2ImplementingInterface" -} -args: null -namedType: "Object2ImplementingInterface" -} -id: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["ID"] -} -} -namedType: $.Scalars["ID"] -args: null -} -boolean: { -type: { -kind: "nullable" -type: { -kind: "named" -named: $.Scalars["Boolean"] -} -} -namedType: $.Scalars["Boolean"] -args: null -} -} + export interface Object2ImplementingInterface { + __typename: { + type: { + kind: 'literal' + value: 'Object2ImplementingInterface' + } + args: null + namedType: 'Object2ImplementingInterface' + } + id: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['ID'] + } + } + namedType: $.Scalars['ID'] + args: null + } + boolean: { + type: { + kind: 'nullable' + type: { + kind: 'named' + named: $.Scalars['Boolean'] + } + } + namedType: $.Scalars['Boolean'] + args: null + } + } } // ------------------------------------------------------------ // @@ -546,12 +547,13 @@ args: null // ------------------------------------------------------------ // export namespace Union { -/** -* Union documentation. -*/ -export interface FooBarUnion { -__unionname: "FooBarUnion" -type: Object.Foo -| Object.Bar + /** + * Union documentation. + */ + export interface FooBarUnion { + __unionname: 'FooBarUnion' + type: + | Object.Foo + | Object.Bar + } } -} \ No newline at end of file From 073fc0e685792bf9c023fa541d4b4c6ff91eb09b Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Wed, 28 Feb 2024 00:28:20 -0500 Subject: [PATCH 58/71] fix build --- tsconfig.build.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.build.json b/tsconfig.build.json index c51514c70..321b962af 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -5,5 +5,5 @@ "rootDir": "src" }, "include": ["src"], - "exclude": ["**/*.spec.*"] + "exclude": ["**/*.test.*", "**/*.spec.*", "**/*.test-d.ts"] } From 9b688d54200b94bfc40e26ea68c717508893a528 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Mon, 11 Mar 2024 22:28:50 -0400 Subject: [PATCH 59/71] work --- src/Schema/Schema.ts | 116 ++-- src/Schema/schema/Field/Field.ts | 38 ++ src/Schema/schema/Field/Type.ts | 38 ++ src/Schema/schema/Field/_.ts | 1 + src/Schema/schema/Field/__.ts | 2 + src/Schema/schema/Index.ts | 17 + src/Schema/schema/NamedType/Enum.ts | 19 + src/Schema/schema/NamedType/Interface.ts | 31 + src/Schema/schema/NamedType/NamedType.ts | 30 + src/Schema/schema/NamedType/Object.ts | 32 ++ src/Schema/schema/NamedType/Scalar/Scalar.ts | 47 ++ src/Schema/schema/NamedType/Scalar/_.ts | 1 + .../NamedType/Scalar/nativeConstructors.ts | 5 + src/Schema/schema/NamedType/Union.ts | 25 + src/Schema/schema/NamedType/_.ts | 6 + src/Schema/schema/NamedType/__.ts | 1 + src/Schema/schema/__.ts | 5 + src/Schema/schema/__namespaced.ts | 3 + src/SelectionSet/SelectionSet.test-d.ts | 3 + src/SelectionSet/SelectionSet.ts | 102 ++-- src/generator/generator.ts | 141 ++--- src/lib/Code.ts | 2 + tests/builder/_/schema.ts | 540 +++--------------- .../__snapshots__/generate.test.ts.snap | 248 ++------ 24 files changed, 611 insertions(+), 842 deletions(-) create mode 100644 src/Schema/schema/Field/Field.ts create mode 100644 src/Schema/schema/Field/Type.ts create mode 100644 src/Schema/schema/Field/_.ts create mode 100644 src/Schema/schema/Field/__.ts create mode 100644 src/Schema/schema/Index.ts create mode 100644 src/Schema/schema/NamedType/Enum.ts create mode 100644 src/Schema/schema/NamedType/Interface.ts create mode 100644 src/Schema/schema/NamedType/NamedType.ts create mode 100644 src/Schema/schema/NamedType/Object.ts create mode 100644 src/Schema/schema/NamedType/Scalar/Scalar.ts create mode 100644 src/Schema/schema/NamedType/Scalar/_.ts create mode 100644 src/Schema/schema/NamedType/Scalar/nativeConstructors.ts create mode 100644 src/Schema/schema/NamedType/Union.ts create mode 100644 src/Schema/schema/NamedType/_.ts create mode 100644 src/Schema/schema/NamedType/__.ts create mode 100644 src/Schema/schema/__.ts create mode 100644 src/Schema/schema/__namespaced.ts diff --git a/src/Schema/Schema.ts b/src/Schema/Schema.ts index 254047d44..a88b3cc56 100644 --- a/src/Schema/Schema.ts +++ b/src/Schema/Schema.ts @@ -1,68 +1,68 @@ -/* eslint-disable @typescript-eslint/ban-types */ +// /* eslint-disable @typescript-eslint/ban-types */ -import type { Digit, Letter } from '../lib/prelude.js' +// import type { Digit, Letter } from '../lib/prelude.js' -export interface Index { - Root: { - Query: null | Object - Mutation: null | Object - Subscription: null | Object - } - objects: Record - unions: { - Union: null | Union - } - scalars: object - unionMemberNames: Record -} +// export interface Index { +// Root: { +// Query: null | Object +// Mutation: null | Object +// Subscription: null | Object +// } +// objects: Record +// unions: { +// Union: null | Union +// } +// scalars: object +// unionMemberNames: Record +// } -// export type Nullable = null -// todo needs to be extensible for custom scalars... -export type Scalar = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] -export type Object = { __typename: FieldTypename } -export type Union = { __unionname: string; type: Object } -export type Interface$ = { __interfacename: string; type: object; implementors: Object } -export type Literal = string -export type Named = Scalar | Object | Union | Literal | Interface$ -export type Node = Object | Union | Scalar // | Nullable +// // export type Nullable = null +// // todo needs to be extensible for custom scalars... +// export type Scalar = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] +// export type Object = { __typename: Schema2.Field.Type.Typename } +// export type Union = { __unionname: string; type: Object } +// export type Interface$ = { __interfacename: string; type: object; implementors: Object } +// export type Literal = string +// export type Named = Scalar | Object | Union | Literal | Interface$ +// export type Node = Object | Union | Scalar // | Nullable -export type FieldTypeNamed = { kind: 'named'; named: any } -export type FieldTypeLiteral = { kind: 'literal'; value: string } -export type FieldTypeList = { kind: 'list'; type: any } -export type FieldTypeNullable = { kind: 'nullable'; type: any } -export type FieldType = FieldTypeNamed | FieldTypeList | FieldTypeNullable | FieldTypeLiteral +// export type FieldTypeNamed = { kind: 'named'; named: any } +// export type FieldTypeLiteral = { kind: 'literal'; value: string } +// export type FieldTypeList = { kind: 'list'; type: any } +// export type FieldTypeNullable = { kind: 'nullable'; type: any } +// export type FieldType = FieldTypeNamed | FieldTypeList | FieldTypeNullable | FieldTypeLiteral -export type Field<$Named extends Named = Named> = { - args: null | FieldArgs - type: FieldType - namedType: $Named -} -export type FieldScalar = Field -export type FieldObject = Field -export type FieldUnion = Field -export type FieldInterface = Field -export type FieldTypename = { args: null; type: FieldTypeLiteral; namedType: string } +// export type Field<$Named extends Named = Named> = { +// args: null | FieldArgs +// type: FieldType +// namedType: $Named +// } +// export type FieldScalar = Field +// export type FieldObject = Field +// export type FieldUnion = Field +// export type FieldInterface = Field +// export type FieldTypename = { args: null; type: FieldTypeLiteral; namedType: string } -export type FieldArgs = { type: object; allOptional: boolean } +// export type FieldArgs = { type: object; allOptional: boolean } -export type AsField = T extends Field ? T : never +// export type AsField = T extends Field ? T : never -/** - * @see http://spec.graphql.org/draft/#sec-Names - */ -// dprint-ignore -export type NameParse = - T extends NameHead ? T : - T extends `${NameHead}${infer Rest}` ? Rest extends NameBodyParse ? T - : never - : never +// /** +// * @see http://spec.graphql.org/draft/#sec-Names +// */ +// // dprint-ignore +// export type NameParse = +// T extends NameHead ? T : +// T extends `${NameHead}${infer Rest}` ? Rest extends NameBodyParse ? T +// : never +// : never -// dprint-ignore -export type NameBodyParse = - S extends NameBody ? S : - S extends `${NameBody}${infer Rest}` ? NameBodyParse extends string ? S - : never - : never +// // dprint-ignore +// export type NameBodyParse = +// S extends NameBody ? S : +// S extends `${NameBody}${infer Rest}` ? NameBodyParse extends string ? S +// : never +// : never -export type NameHead = Letter | '_' -export type NameBody = Letter | '_' | Digit +// export type NameHead = Letter | '_' +// export type NameBody = Letter | '_' | Digit diff --git a/src/Schema/schema/Field/Field.ts b/src/Schema/schema/Field/Field.ts new file mode 100644 index 000000000..379b14413 --- /dev/null +++ b/src/Schema/schema/Field/Field.ts @@ -0,0 +1,38 @@ +import type { Scalar } from '../NamedType/Scalar/_.js' +import type { Any, List, Named, Nullable, Unwrap } from './Type.js' +import { unwrap } from './Type.js' + +export type As = T extends Field ? T : never + +export type Scalar<$Args extends Args | null = null> = Field, $Args> + +export type String<$Args extends Args | null = null> = Field, $Args> + +export type Number<$Args extends Args | null = null> = Field, $Args> + +export type Boolean<$Args extends Args | null = null> = Field, $Args> + +type InputFieldType = Scalar.Any | List | Nullable + +export interface Args<$Fields extends Record = Record> { + allOptional: false // todo + fields: $Fields +} + +export const field = <$Type extends Any, $Args extends null | Args = null>( + type: $Type, + args: $Args = null as $Args, +): Field<$Type, $Args> => { + // @ts-expect-error fixme + return { + typeUnwrapped: unwrap(type), + type, + args, + } +} + +export type Field<$Type extends Any = Any, $Args extends Args | null = Args | null> = { + typeUnwrapped: Unwrap<$Type> + type: $Type + args: $Args +} diff --git a/src/Schema/schema/Field/Type.ts b/src/Schema/schema/Field/Type.ts new file mode 100644 index 000000000..9ddaf8b93 --- /dev/null +++ b/src/Schema/schema/Field/Type.ts @@ -0,0 +1,38 @@ +import type { NamedType } from '../NamedType/__.js' + +export interface __typename<$Type extends string = string> { + kind: 'typename' + type: $Type +} +export interface Nullable<$Type> { + kind: 'nullable' + type: $Type +} +export interface List<$Type> { + kind: 'list' + type: $Type +} +export interface Named<$Type extends NamedType.Any = NamedType.Any> { + kind: 'named' + type: $Type +} +export type Any = List | __typename | Nullable | Named + +export const __typename = <$Type extends string>(type: $Type): __typename<$Type> => ({ kind: `typename`, type }) +export const nullable = <$Type extends __typename | List>(type: $Type): Nullable<$Type> => ({ + kind: `nullable`, + type, +}) +export const list = <$Type extends Any>(type: $Type): List<$Type> => ({ kind: `list`, type }) +export const named = <$Type extends NamedType.Any>(type: $Type): Named<$Type> => ({ kind: `named`, type }) + +// dprint-ignore +export type Unwrap<$Type extends Any> = + $Type extends Named ? $Type['type'] : + $Type extends __typename ? $Type['type'] : + Unwrap<$Type['type']> + +export const unwrap = <$Type extends Any>(type: $Type): Unwrap<$Type> => { + // @ts-expect-error fixme + return type.kind === `named` ? type.type : unwrap(type.type) +} diff --git a/src/Schema/schema/Field/_.ts b/src/Schema/schema/Field/_.ts new file mode 100644 index 000000000..f9956dd53 --- /dev/null +++ b/src/Schema/schema/Field/_.ts @@ -0,0 +1 @@ +export * as Field from './__.js' diff --git a/src/Schema/schema/Field/__.ts b/src/Schema/schema/Field/__.ts new file mode 100644 index 000000000..3292e0087 --- /dev/null +++ b/src/Schema/schema/Field/__.ts @@ -0,0 +1,2 @@ +export * from './Field.js' +export * from './Type.js' diff --git a/src/Schema/schema/Index.ts b/src/Schema/schema/Index.ts new file mode 100644 index 000000000..fa5fa22f4 --- /dev/null +++ b/src/Schema/schema/Index.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import type { Object, Union } from './__.js' + +export interface Index { + Root: { + Query: null | Object + Mutation: null | Object + Subscription: null | Object + } + objects: Record + unions: { + Union: null | Union + } + scalars: object + unionMemberNames: Record +} diff --git a/src/Schema/schema/NamedType/Enum.ts b/src/Schema/schema/NamedType/Enum.ts new file mode 100644 index 000000000..cec53ca24 --- /dev/null +++ b/src/Schema/schema/NamedType/Enum.ts @@ -0,0 +1,19 @@ +import type { __typename } from '../__.js' + +export interface Enum< + $Name extends string = string, + $Members extends [string, ...string[]] = [string, ...string[]], +> { + kind: 'Enum' + name: $Name + members: $Members +} + +export const Enum = <$Name extends string, $Members extends [string, ...string[]]>( + name: $Name, + members: $Members, +): Enum<$Name, $Members> => ({ + kind: `Enum`, + name, + members, +}) diff --git a/src/Schema/schema/NamedType/Interface.ts b/src/Schema/schema/NamedType/Interface.ts new file mode 100644 index 000000000..59cf32ff4 --- /dev/null +++ b/src/Schema/schema/NamedType/Interface.ts @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import type { __typename } from '../__.js' +import type { Field } from '../Field/Field.js' +import type { Object } from './Object.js' + +export type Interface< + $Name extends string = string, + $Fields extends Record> = Record>, + $Implementors extends [Object, ...Object[]] = [Object, ...Object[]], +> = { + kind: 'Interface' + name: $Name + fields: $Fields + implementors: $Implementors +} + +export const Interface = < + $Name extends string, + $Fields extends Record, + $Implementors extends [Object, ...Object[]], +>( + name: $Name, + fields: $Fields, + implementors: $Implementors, +): Interface<$Name, $Fields, $Implementors> => ({ + kind: `Interface`, + name, + fields, + implementors, +}) diff --git a/src/Schema/schema/NamedType/NamedType.ts b/src/Schema/schema/NamedType/NamedType.ts new file mode 100644 index 000000000..a5d17c781 --- /dev/null +++ b/src/Schema/schema/NamedType/NamedType.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import type { Digit, Letter } from '../../../lib/prelude.js' +import type { Enum } from './Enum.js' +import type { Interface } from './Interface.js' +import type { Object } from './Object.js' +import type { Scalar } from './Scalar/_.js' +import type { Union } from './Union.js' + +export type Any = Interface | Enum | Object | Scalar.Any | Union + +/** + * @see http://spec.graphql.org/draft/#sec-Names + */ +// dprint-ignore +export type NameParse = + T extends NameHead ? T : + T extends `${NameHead}${infer Rest}` ? Rest extends NameBodyParse ? T + : never + : never + +// dprint-ignore +export type NameBodyParse = + S extends NameBody ? S : + S extends `${NameBody}${infer Rest}` ? NameBodyParse extends string ? S + : never + : never + +export type NameHead = Letter | '_' +export type NameBody = Letter | '_' | Digit diff --git a/src/Schema/schema/NamedType/Object.ts b/src/Schema/schema/NamedType/Object.ts new file mode 100644 index 000000000..efd3b53b6 --- /dev/null +++ b/src/Schema/schema/NamedType/Object.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import type { Field } from '../Field/Field.js' +import { field } from '../Field/Field.js' +import { __typename } from '../Field/Type.js' + +export type Fields = Record> + +export type ObjectFields = { + __typename: Field<__typename> +} & Fields + +export interface Object< + $Name extends string = string, + $Fields extends Fields = Fields, +> { + kind: 'Object' + fields: { + __typename: Field<__typename<$Name>> + } & $Fields +} + +export const Object = <$Name extends string, $Fields extends Record>( + name: $Name, + fields: $Fields, +): Object<$Name, $Fields> => ({ + kind: `Object`, + fields: { + __typename: field(__typename(name)), + ...fields, + }, +}) diff --git a/src/Schema/schema/NamedType/Scalar/Scalar.ts b/src/Schema/schema/NamedType/Scalar/Scalar.ts new file mode 100644 index 000000000..acee465a6 --- /dev/null +++ b/src/Schema/schema/NamedType/Scalar/Scalar.ts @@ -0,0 +1,47 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import { nativeScalarConstructors } from './nativeConstructors.js' + +export const ScalarKind = `Scalar` as const + +export type ScalarKind = typeof ScalarKind + +export const scalar = <$Name extends string, $TypeConstructor extends () => string | number | boolean>( + name: $Name, + constructor: $TypeConstructor, +): Scalar<$Name, $TypeConstructor> => ({ + kind: ScalarKind, + name: name, + constructor: constructor as any, // eslint-disable-line +}) + +export interface Scalar< + $Name extends string = string, + $TypeConstructor extends () => string | number | boolean = () => string | number | boolean, +> { + kind: ScalarKind + name: $Name + constructor: $TypeConstructor +} + +export const String = scalar(`String`, nativeScalarConstructors.String) + +export const ID = scalar(`ID`, nativeScalarConstructors.String) + +export const Int = scalar(`Int`, nativeScalarConstructors.Number) + +export const Float = scalar(`Float`, nativeScalarConstructors.Number) + +export const Boolean = scalar(`Boolean`, nativeScalarConstructors.Boolean) + +export type ID = typeof ID + +export type String = typeof String + +export type Int = typeof Int + +export type Boolean = typeof Boolean + +export type Float = typeof Float + +export type Any = String | Int | Boolean | ID | Float diff --git a/src/Schema/schema/NamedType/Scalar/_.ts b/src/Schema/schema/NamedType/Scalar/_.ts new file mode 100644 index 000000000..e21bb3cfa --- /dev/null +++ b/src/Schema/schema/NamedType/Scalar/_.ts @@ -0,0 +1 @@ +export * as Scalar from './Scalar.js' diff --git a/src/Schema/schema/NamedType/Scalar/nativeConstructors.ts b/src/Schema/schema/NamedType/Scalar/nativeConstructors.ts new file mode 100644 index 000000000..5eeef04ac --- /dev/null +++ b/src/Schema/schema/NamedType/Scalar/nativeConstructors.ts @@ -0,0 +1,5 @@ +export const nativeScalarConstructors = { + String: String, + Number: Number, + Boolean: Boolean, +} diff --git a/src/Schema/schema/NamedType/Union.ts b/src/Schema/schema/NamedType/Union.ts new file mode 100644 index 000000000..e3a1eaa4e --- /dev/null +++ b/src/Schema/schema/NamedType/Union.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import type { __typename } from '../__.js' +import type { Object } from './Object.js' + +export type Union< + $Name extends string = string, + $Members extends [Object, ...Object[]] = [Object, ...Object[]], +> = { + kind: `Union` + name: $Name + members: $Members +} + +export const Union = < + $Name extends string, + $Members extends [Object, ...Object[]], +>( + name: $Name, + members: $Members, +): Union<$Name, $Members> => ({ + kind: `Union`, + name, + members, +}) diff --git a/src/Schema/schema/NamedType/_.ts b/src/Schema/schema/NamedType/_.ts new file mode 100644 index 000000000..c2650fcae --- /dev/null +++ b/src/Schema/schema/NamedType/_.ts @@ -0,0 +1,6 @@ +export * from './Enum.js' +export * from './Interface.js' +export * from './NamedType.js' +export * from './Object.js' +export * from './Scalar/_.js' +export * from './Union.js' diff --git a/src/Schema/schema/NamedType/__.ts b/src/Schema/schema/NamedType/__.ts new file mode 100644 index 000000000..974d0f166 --- /dev/null +++ b/src/Schema/schema/NamedType/__.ts @@ -0,0 +1 @@ +export * as NamedType from './_.js' diff --git a/src/Schema/schema/__.ts b/src/Schema/schema/__.ts new file mode 100644 index 000000000..d41375060 --- /dev/null +++ b/src/Schema/schema/__.ts @@ -0,0 +1,5 @@ +export * as Schema from './__namespaced.js' +export { Args, As, Field } from './Field/__.js' +export { __typename, List, Named, Nullable } from './Field/Type.js' +export { Index } from './Index.js' +export * from './NamedType/_.js' diff --git a/src/Schema/schema/__namespaced.ts b/src/Schema/schema/__namespaced.ts new file mode 100644 index 000000000..d8ec79685 --- /dev/null +++ b/src/Schema/schema/__namespaced.ts @@ -0,0 +1,3 @@ +export * from './Field/_.js' +export { Index } from './Index.js' +export * as Named from './NamedType/_.js' diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 46fcf9c6d..f7b05912c 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -14,11 +14,14 @@ test(`ParseAliasExpression`, () => { }) test(`Query`, () => { + assertType({ __typename: true }) + // @ts-expect-error directive on root type Query assertType({ $defer: true }) // Scalar assertType({ id: true }) + assertType({ id: true }) assertType({ id: false }) assertType({ id: 1 }) assertType({ id: 0 }) diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 672c735f6..b4eb1d3c8 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -2,29 +2,35 @@ import type { MaybeList, StringNonEmpty, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' -import type { Schema } from '../Schema/__.js' +import type { Schema } from '../Schema/schema/__.js' -export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Object +export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Named.Object ? Object<$Index['Root']['Query'], $Index> : never -export type Mutation<$Index extends Schema.Index> = $Index['Root']['Mutation'] extends Schema.Object +export type Mutation<$Index extends Schema.Index> = $Index['Root']['Mutation'] extends Schema.Named.Object ? Object<$Index['Root']['Mutation'], $Index> : never -export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscription'] extends Schema.Object +export type Subscription<$Index extends Schema.Index> = $Index['Root']['Subscription'] extends Schema.Named.Object ? Object<$Index['Root']['Subscription'], $Index> : never // dprint-ignore type Object< - $Object extends Schema.Object, + $Fields extends Schema.Named.Object, + $Index extends Schema.Index, +> = Fields<$Fields['fields'], $Index> + +// dprint-ignore +type Fields< + $Fields extends Schema.Named.Fields, $Index extends Schema.Index, > = & { - [Key in keyof $Object]?: - Field, $Index> + [Key in keyof $Fields]?: + Field, $Index> } & /** @@ -33,9 +39,9 @@ type Object< */ { [ - Key in keyof $Object as `${keyof $Object & string}_as_${StringNonEmpty}` + Key in keyof $Fields as `${keyof $Fields & string}_as_${StringNonEmpty}` ]?: - Field, $Index> + Field, $Index> } & /** @@ -43,7 +49,7 @@ type Object< * @see https://spec.graphql.org/draft/#sec-Inline-Fragments */ { - ___?: MaybeList & FieldDirectives> + ___?: MaybeList & FieldDirectives> } & /** @@ -57,51 +63,49 @@ export type IsSelectScalarsWildcard = SS extends { $scalars: ClientIndicator // dprint-ignore export type Field< - $Field extends Schema.Field, + $Field extends Schema.Field.Field, $Index extends Schema.Index, > = - $Field extends Schema.FieldTypename ? NoArgsIndicator : - $Field extends Schema.FieldScalar ? Indicator<$Field> : - $Field extends Schema.FieldUnion ? Union<$Field['namedType'], $Index> : - $Field extends Schema.FieldInterface ? Interface$<$Field['namedType'], $Index> : - $Field extends Schema.FieldObject ? Object<$Field['namedType'], $Index> & Arguments<$Field> & FieldDirectives - : TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> - -type Arguments<$Field extends Schema.Field> = $Field['args'] extends Schema.FieldArgs - ? $Field['args']['allOptional'] extends true ? { - $?: $Field['args']['type'] - } - : { - $: $Field['args']['type'] - } - : {} + $Field['type']['kind'] extends 'typename' ? NoArgsIndicator : + // @ts-expect-error fixme? + $Field['typeUnwrapped']['kind'] extends 'Scalar' ? Indicator<$Field['typeUnwrapped']> : + $Field['typeUnwrapped']['kind'] extends 'Enum' ? Indicator<$Field['typeUnwrapped']> : + $Field['typeUnwrapped']['kind'] extends 'Object' ? Object<$Field['typeUnwrapped'], $Index> & FieldDirectives & Arguments<$Field> : + $Field['typeUnwrapped']['kind'] extends 'Union' ? Union<$Field['typeUnwrapped'], $Index> : + $Field['typeUnwrapped']['kind'] extends 'Interface' ? Interface<$Field['typeUnwrapped'], $Index> : + TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> +// dprint-ignore +type Arguments<$Field extends Schema.Field.Field> = +$Field['args'] extends Schema.Field.Args ? $Field['args']['allOptional'] extends true ? { $?: $Field['args']['fields'] } : + { $: $Field['args']['fields'] } : + {} // dprint-ignore -type Interface$< - $Node extends Schema.Interface$, +type Interface< + $Node extends Schema.Named.Interface, $Index extends Schema.Index, > = -& Object< - & $Node['type'] - & { - __typename: $Node['implementors']['__typename'] - }, - $Index - > -& { - [Key in $Node['implementors']['__typename']['namedType'] as `on${Capitalize}`]?: - Object, $Index> & FieldDirectives - } + & Fields< + & $Node['fields'] + & { + __typename: $Node['implementors'][number]['fields']['__typename'] + }, + $Index + > + & { + [Key in $Node['implementors'][number]['fields']['__typename']['typeUnwrapped'] as `on${Capitalize}`]?: + Object, $Index> & FieldDirectives + } // TODO why does $object not get passed to this in a distributed way? // dprint-ignore type Union< - $Union extends Schema.Union, + $Union extends Schema.Named.Union, $Index extends Schema.Index, > = & { - [Key in $Union['type']['__typename']['namedType'] as `on${Capitalize}`]?: - Object, $Index> & FieldDirectives + [Key in $Union['members'][number]['fields']['__typename']['typeUnwrapped'] as `on${Capitalize}`]?: + Object, $Index> & FieldDirectives } & { __typename?: NoArgsIndicator @@ -137,8 +141,8 @@ export interface Alias { // dprint-ignore export type ParseAliasExpression = - E extends `${infer O}_as_${infer T}` ? Schema.NameParse extends never ? E : - Schema.NameParse extends never ? E : + E extends `${infer O}_as_${infer T}` ? Schema.Named.NameParse extends never ? E : + Schema.Named.NameParse extends never ? E : Alias : E @@ -207,11 +211,11 @@ export type OmitNegativeIndicators<$SelectionSet> = { export type NoArgsIndicator = ClientIndicator | FieldDirectives // dprint-ignore -export type Indicator<$Field extends Schema.FieldScalar = Schema.FieldScalar> = - $Field['args'] extends Schema.FieldArgs ? $Field['args']['allOptional'] extends true - ? ({ $?: $Field['args']['type'] } & FieldDirectives) | ClientIndicator - : { $: $Field['args']['type'] } & FieldDirectives - : NoArgsIndicator +export type Indicator<$Field extends Schema.Field.Scalar = Schema.Field.Scalar> = + $Field['args'] extends Schema.Field.Args ? $Field['args']['allOptional'] extends true + ? ({ $?: $Field['args']['fields'] } & FieldDirectives) | ClientIndicator + : { $: $Field['args']['fields'] } & FieldDirectives + : NoArgsIndicator /** * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 91e54e35a..19d3add8c 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -4,9 +4,10 @@ import type { GraphQLInputObjectType, GraphQLInterfaceType, GraphQLNamedType, + GraphQLObjectType, GraphQLSchema, } from 'graphql' -import { GraphQLNonNull, GraphQLObjectType, isEnumType, isListType, isNamedType, isNonNullType } from 'graphql' +import { GraphQLNonNull, isEnumType, isListType, isNamedType } from 'graphql' import { buildSchema } from 'graphql' import _ from 'json-bigint' import fs from 'node:fs/promises' @@ -111,7 +112,7 @@ const pointerRenderers = defineReferenceRenderers({ GraphQLInterfaceType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), GraphQLList: (config, node) => Code.list(dispatchToReferenceRenderer(config, node.ofType)), GraphQLObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), - GraphQLScalarType: (_, node) => `$.Scalars[${Code.quote(node.name)}]`, + GraphQLScalarType: (_, node) => `_.Scalar.${node.name}`, GraphQLUnionType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), }) @@ -120,9 +121,9 @@ const concreteRenderers = defineConcreteRenderers({ Code.TSDoc( getDocumentation(config, node), Code.export$( - Code.union( + Code.type( node.name, - node.getValues().map((_) => Code.quote(_.name)), + `_.Enum<${Code.quote(node.name)}, ${Code.tuple(node.getValues().map((_) => Code.quote(_.name)))} >`, ), ), ), @@ -137,40 +138,35 @@ const concreteRenderers = defineConcreteRenderers({ ) return Code.TSDoc( getDocumentation(config, node), - Code.export$(Code.interface$( + Code.export$(Code.type( node.name, - Code.objectFrom({ - __interfacename: Code.quote(node.name), - type: renderFields(config, node), - implementors: Code.unionItems(implementors.map(_ => dispatchToReferenceRenderer(config, _))), - }), + `_.Interface<${Code.quote(node.name)}, ${renderFields(config, node)}, ${ + Code.tuple(implementors.map(_ => `Object.${_.name}`)) + }>`, )), ) }, GraphQLObjectType: (config, node) => Code.TSDoc( getDocumentation(config, node), - Code.export$(Code.interface$(node.name, renderFields(config, node))), + Code.export$(Code.type(node.name, `_.Object<${Code.quote(node.name)}, ${renderFields(config, node)}>`)), ), GraphQLScalarType: () => ``, GraphQLUnionType: (config, node) => Code.TSDoc( getDocumentation(config, node), Code.export$( - Code.interface$( + Code.type( node.name, - Code.objectFrom({ - __unionname: Code.quote(node.name), - type: { - type: Code.unionItems( - node - .getTypes() - .map( - (_) => dispatchToReferenceRenderer(config, _), - ), - ), - }, - }), + `_.Union<${Code.quote(node.name)},${ + Code.tuple( + node + .getTypes() + .map( + (_) => dispatchToReferenceRenderer(config, _), + ), + ) + }>`, ), ), ), @@ -227,23 +223,7 @@ const getDocumentation = (config: Config, node: Describable) => { const defaultDescription = (node: Describable) => `There is no documentation for this ${getNodeDisplayName(node)}.` const renderFields = (config: Config, node: AnyGraphQLFieldsType): string => { - const __typenameField = node instanceof GraphQLObjectType - ? [ - Code.field( - `__typename`, - Code.objectFrom({ - type: Code.objectFrom({ - kind: Code.quote(`literal`), - value: Code.quote(node.name), - }), - args: null, - namedType: Code.quote(node.name), - }), - ), - ] - : [] return Code.object(Code.fields([ - ...__typenameField, ...values(node.getFields()).map((field) => Code.TSDoc( getDocumentation(config, field), @@ -257,77 +237,62 @@ const buildType = (config: Config, node: AnyClass) => { const { node: nodeInner, nullable } = unwrapNonNull(node) if (isNamedType(nodeInner)) { - const namedType = dispatchToReferenceRenderer(config, nodeInner) - const type = Code.objectFrom({ - kind: Code.quote(`named`), - named: namedType, - }) + const namedTypeReference = dispatchToReferenceRenderer(config, nodeInner) + const namedTypeCode = `_.Named<${namedTypeReference}>` return nullable - ? Code.objectFrom({ - kind: Code.quote(`nullable`), - type: type, - }) - : type + ? `_.Nullable<${namedTypeCode}>` + : namedTypeCode } if (isListType(nodeInner)) { - const nodeType = Code.objectFrom({ - kind: Code.quote(`list`), - type: buildType(config, nodeInner.ofType), - }) + const fieldType = `_.List<${buildType(config, nodeInner.ofType)}>` as any as string return nullable - ? Code.objectFrom({ - kind: Code.quote(`nullable`), - type: nodeType, - }) - : nodeType + ? `_.Nullable<${fieldType}>` + : fieldType } throw new Error(`Unhandled type: ${String(node)}`) } -const getNamedType = (config: Config, node: AnyClass): GraphQLNamedType => { - if (isNamedType(node)) return node - if (isNonNullType(node)) return getNamedType(config, node.ofType) - if (isListType(node)) return getNamedType(config, node.ofType) - throw new Error(`Unhandled type: ${String(node)}`) -} +// const getNamedType = (config: Config, node: AnyClass): GraphQLNamedType => { +// if (isNamedType(node)) return node +// if (isNonNullType(node)) return getNamedType(config, node.ofType) +// if (isListType(node)) return getNamedType(config, node.ofType) +// throw new Error(`Unhandled type: ${String(node)}`) +// } const renderField = (config: Config, field: AnyField): string => { const type = buildType(config, field.type) - const namedType = dispatchToReferenceRenderer(config, getNamedType(config, field.type)) const args = isGraphQLOutputField(field) && field.args.length > 0 ? renderArgs(config, field.args) : null - return Code.objectFrom({ - type, - namedType, - args, - }) + return `_.Field<${type}${args ? `, ${args}` : ``}>` } const renderArgs = (config: Config, args: readonly GraphQLArgument[]) => { let hasRequiredArgs = false - const argsRendered = Code.object( - Code.fields( - args.map((arg) => { - const { node, nullable } = unwrapNonNull(arg.type) - hasRequiredArgs = hasRequiredArgs || !nullable - return Code.field( - arg.name, - nullable - ? Code.nullable(dispatchToReferenceRenderer(config, node)) - : dispatchToReferenceRenderer(config, node), - { optional: nullable }, - ) - }), - ), - ) + const argsRendered = `_.Args<${ + Code.object( + Code.fields( + args.map((arg) => { + const { node, nullable } = unwrapNonNull(arg.type) + hasRequiredArgs = hasRequiredArgs || !nullable + return Code.field( + arg.name, + nullable + ? `_.Nullable<${dispatchToReferenceRenderer(config, node)}>` + : dispatchToReferenceRenderer(config, node), + ) + }), + ), + ) + }>` + return argsRendered return Code.objectFrom({ type: { type: argsRendered }, - allOptional: { type: !hasRequiredArgs }, + // allOptional: { type: !hasRequiredArgs }, }) } @@ -392,6 +357,8 @@ export const generateCode = (input: Input) => { let code = `` + code += `import type * as _ from '../../../src/Schema/schema2/__.js'\n\n` + code += Code.export$( Code.namespace( `$`, diff --git a/src/lib/Code.ts b/src/lib/Code.ts index e9dd01dd3..4bcc8af85 100644 --- a/src/lib/Code.ts +++ b/src/lib/Code.ts @@ -4,6 +4,7 @@ export namespace Code { export const nullable = (type: string) => `${type} | null` export const union = (name: string, types: string[]) => `type ${name} =\n| ${Code.unionItems(types)}` export const unionItems = (types: string[]) => types.join(`\n| `) + export const tuple = (types: string[]) => `[${types.join(`, `)}]` export const list = (type: string) => `Array<${type}>` export const field = (name: string, type: string, options?: { optional?: boolean }) => { if (options?.optional) return `${name}?: ${type}` @@ -31,6 +32,7 @@ export namespace Code { ) => Code.field(name, String(spec.type), { optional: spec.optional })), ), ) + export const type = (name: string, type: string) => `type ${name} = ${type}` export const interface$ = (name: string, object: string) => `interface ${name} ${object}` export const export$ = (thing: string) => `export ${thing}` export const TSDoc = (content: string | null, block: string) => diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index f70ae1226..801662a12 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -1,3 +1,5 @@ +import type * as _ from '../../../src/Schema/schema/__.js' + export namespace $ { export interface Index { Root: { @@ -37,233 +39,51 @@ export namespace $ { // ------------------------------------------------------------ // export namespace Root { - export interface Query { - __typename: { - type: { - kind: 'literal' - value: 'Query' - } - args: null - namedType: 'Query' - } - interface: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: Interface.Interface - } - } - namedType: Interface.Interface - args: null - } - id: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['ID'] - } - } - namedType: $.Scalars['ID'] - args: null - } - idNonNull: { - type: { - kind: 'named' - named: $.Scalars['ID'] - } - namedType: $.Scalars['ID'] - args: null - } - string: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['String'] - } - } - namedType: $.Scalars['String'] - args: null - } - stringWithRequiredArg: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['String'] - } - } - namedType: $.Scalars['String'] - args: { - type: { - string: $.Scalars['String'] - } - allOptional: false - } - } - stringWithArgs: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['String'] - } - } - namedType: $.Scalars['String'] - args: { - type: { - string?: $.Scalars['String'] | null - int?: $.Scalars['Int'] | null - float?: $.Scalars['Float'] | null - boolean?: $.Scalars['Boolean'] | null - id?: $.Scalars['ID'] | null - } - allOptional: true - } - } - object: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: Object.Object - } - } - namedType: Object.Object - args: null - } - listListIntNonNull: { - type: { - kind: 'list' - type: { - kind: 'list' - type: { - kind: 'named' - named: $.Scalars['Int'] - } - } - } - namedType: $.Scalars['Int'] - args: null - } - listListInt: { - type: { - kind: 'nullable' - type: { - kind: 'list' - type: { - kind: 'nullable' - type: { - kind: 'list' - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['Int'] - } - } - } - } - } - } - namedType: $.Scalars['Int'] - args: null - } - listInt: { - type: { - kind: 'nullable' - type: { - kind: 'list' - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['Int'] - } - } - } - } - namedType: $.Scalars['Int'] - args: null - } - listIntNonNull: { - type: { - kind: 'list' - type: { - kind: 'named' - named: $.Scalars['Int'] - } - } - namedType: $.Scalars['Int'] - args: null - } - objectNested: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: Object.ObjectNested - } - } - namedType: Object.ObjectNested - args: null - } - objectNonNull: { - type: { - kind: 'named' - named: Object.Object - } - namedType: Object.Object - args: null - } - objectWithArgs: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: Object.Object - } - } - namedType: Object.Object - args: { - type: { - string?: $.Scalars['String'] | null - int?: $.Scalars['Int'] | null - float?: $.Scalars['Float'] | null - boolean?: $.Scalars['Boolean'] | null - id?: $.Scalars['ID'] | null - } - allOptional: true - } - } - fooBarUnion: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: Union.FooBarUnion - } - } - namedType: Union.FooBarUnion - args: null - } + export type Query = _.Object<'Query', { + interface: _.Field<_.Nullable<_.Named>> + id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + idNonNull: _.Field<_.Named<_.Scalar.ID>> + string: _.Field<_.Nullable<_.Named<_.Scalar.String>>> + stringWithRequiredArg: _.Field< + _.Nullable<_.Named<_.Scalar.String>>, + _.Args<{ + string: _.Scalar.String + }> + > + stringWithArgs: _.Field< + _.Nullable<_.Named<_.Scalar.String>>, + _.Args<{ + string: _.Nullable<_.Scalar.String> + int: _.Nullable<_.Scalar.Int> + float: _.Nullable<_.Scalar.Float> + boolean: _.Nullable<_.Scalar.Boolean> + id: _.Nullable<_.Scalar.ID> + }> + > + object: _.Field<_.Nullable<_.Named>> + listListIntNonNull: _.Field<_.List<_.List<_.Named<_.Scalar.Int>>>> + listListInt: _.Field<_.Nullable<_.List<_.Nullable<_.List<_.Nullable<_.Named<_.Scalar.Int>>>>>>> + listInt: _.Field<_.Nullable<_.List<_.Nullable<_.Named<_.Scalar.Int>>>>> + listIntNonNull: _.Field<_.List<_.Named<_.Scalar.Int>>> + objectNested: _.Field<_.Nullable<_.Named>> + objectNonNull: _.Field<_.Named> + objectWithArgs: _.Field< + _.Nullable<_.Named>, + _.Args<{ + string: _.Nullable<_.Scalar.String> + int: _.Nullable<_.Scalar.Int> + float: _.Nullable<_.Scalar.Float> + boolean: _.Nullable<_.Scalar.Boolean> + id: _.Nullable<_.Scalar.ID> + }> + > + fooBarUnion: _.Field<_.Nullable<_.Named>> /** * Query enum field documentation. */ - abcEnum: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: Enum.ABCEnum - } - } - namedType: Enum.ABCEnum - args: null - } - } + abcEnum: _.Field<_.Nullable<_.Named>> + }> + type x = Query['fields']['fooBarUnion']['typeUnwrapped']['members'][number]['fields']['__typename']['typeUnwrapped'] } // ------------------------------------------------------------ // @@ -279,10 +99,7 @@ export namespace Enum { * "B" - Enum B member documentation. * "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) */ - export type ABCEnum = - | 'A' - | 'B' - | 'C' + export type ABCEnum = _.Enum<'ABCEnum', ['A', 'B', 'C']> } // ------------------------------------------------------------ // @@ -298,25 +115,9 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { - export interface Interface { - __interfacename: 'Interface' - type: { - id: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['ID'] - } - } - namedType: $.Scalars['ID'] - args: null - } - } - implementors: - | Object.Object1ImplementingInterface - | Object.Object2ImplementingInterface - } + export type Interface = _.Interface<'Interface', { + id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + }, [Object.Object1ImplementingInterface, Object.Object2ImplementingInterface]> } // ------------------------------------------------------------ // @@ -327,219 +128,41 @@ export namespace Object { /** * Object documentation. */ - export interface Foo { - __typename: { - type: { - kind: 'literal' - value: 'Foo' - } - args: null - namedType: 'Foo' - } + export type Foo = _.Object<'Foo', { /** * Field documentation. * * @deprecated Field a is deprecated. */ - id: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['ID'] - } - } - namedType: $.Scalars['ID'] - args: null - } - } - - export interface Bar { - __typename: { - type: { - kind: 'literal' - value: 'Bar' - } - args: null - namedType: 'Bar' - } - int: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['Int'] - } - } - namedType: $.Scalars['Int'] - args: null - } - } - - export interface ObjectNested { - __typename: { - type: { - kind: 'literal' - value: 'ObjectNested' - } - args: null - namedType: 'ObjectNested' - } - id: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['ID'] - } - } - namedType: $.Scalars['ID'] - args: null - } - object: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: Object.Object - } - } - namedType: Object.Object - args: null - } - } - - export interface Object { - __typename: { - type: { - kind: 'literal' - value: 'Object' - } - args: null - namedType: 'Object' - } - string: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['String'] - } - } - namedType: $.Scalars['String'] - args: null - } - int: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['Int'] - } - } - namedType: $.Scalars['Int'] - args: null - } - float: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['Float'] - } - } - namedType: $.Scalars['Float'] - args: null - } - boolean: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['Boolean'] - } - } - namedType: $.Scalars['Boolean'] - args: null - } - id: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['ID'] - } - } - namedType: $.Scalars['ID'] - args: null - } - } - - export interface Object1ImplementingInterface { - __typename: { - type: { - kind: 'literal' - value: 'Object1ImplementingInterface' - } - args: null - namedType: 'Object1ImplementingInterface' - } - id: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['ID'] - } - } - namedType: $.Scalars['ID'] - args: null - } - int: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['Int'] - } - } - namedType: $.Scalars['Int'] - args: null - } - } - - export interface Object2ImplementingInterface { - __typename: { - type: { - kind: 'literal' - value: 'Object2ImplementingInterface' - } - args: null - namedType: 'Object2ImplementingInterface' - } - id: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['ID'] - } - } - namedType: $.Scalars['ID'] - args: null - } - boolean: { - type: { - kind: 'nullable' - type: { - kind: 'named' - named: $.Scalars['Boolean'] - } - } - namedType: $.Scalars['Boolean'] - args: null - } - } + id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + }> + + export type Bar = _.Object<'Bar', { + int: _.Field<_.Nullable<_.Named<_.Scalar.Int>>> + }> + + export type ObjectNested = _.Object<'ObjectNested', { + id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + object: _.Field<_.Nullable<_.Named>> + }> + + export type Object = _.Object<'Object', { + string: _.Field<_.Nullable<_.Named<_.Scalar.String>>> + int: _.Field<_.Nullable<_.Named<_.Scalar.Int>>> + float: _.Field<_.Nullable<_.Named<_.Scalar.Float>>> + boolean: _.Field<_.Nullable<_.Named<_.Scalar.Boolean>>> + id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + }> + + export type Object1ImplementingInterface = _.Object<'Object1ImplementingInterface', { + id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + int: _.Field<_.Nullable<_.Named<_.Scalar.Int>>> + }> + + export type Object2ImplementingInterface = _.Object<'Object2ImplementingInterface', { + id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + boolean: _.Field<_.Nullable<_.Named<_.Scalar.Boolean>>> + }> } // ------------------------------------------------------------ // @@ -550,10 +173,5 @@ export namespace Union { /** * Union documentation. */ - export interface FooBarUnion { - __unionname: 'FooBarUnion' - type: - | Object.Foo - | Object.Bar - } + export type FooBarUnion = _.Union<'FooBarUnion', [Object.Foo, Object.Bar]> } diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index adc7212c7..a34877c57 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -1,7 +1,8 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`generates types from GraphQL SDL file 1`] = ` -"export namespace $ { +"import { __typename, Nullable } from '../../../src/Schema/schema2.js' +export namespace $ { export interface Index { Root: { Query: Root.Query @@ -40,33 +41,20 @@ Boolean: boolean export namespace Root { export interface Query { -__typename: { -type: { -kind: "literal" -value: "Query" -} -args: null -namedType: "Query" -} +__typename: __typename<"Query"> interface: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: Interface.Interface -} -} +}> namedType: Interface.Interface args: null } id: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["ID"] -} -} +}> namedType: $.Scalars["ID"] args: null } @@ -79,24 +67,18 @@ namedType: $.Scalars["ID"] args: null } string: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["String"] -} -} +}> namedType: $.Scalars["String"] args: null } stringWithRequiredArg: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["String"] -} -} +}> namedType: $.Scalars["String"] args: { type: { @@ -106,13 +88,10 @@ allOptional: false } } stringWithArgs: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["String"] -} -} +}> namedType: $.Scalars["String"] args: { type: { @@ -126,13 +105,10 @@ allOptional: true } } object: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: Object.Object -} -} +}> namedType: Object.Object args: null } @@ -159,13 +135,10 @@ type: { kind: "nullable" type: { kind: "list" -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["Int"] -} -} +}> } } } @@ -178,13 +151,10 @@ type: { kind: "nullable" type: { kind: "list" -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["Int"] -} -} +}> } } namedType: $.Scalars["Int"] @@ -202,13 +172,10 @@ namedType: $.Scalars["Int"] args: null } objectNested: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: Object.ObjectNested -} -} +}> namedType: Object.ObjectNested args: null } @@ -221,13 +188,10 @@ namedType: Object.Object args: null } objectWithArgs: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: Object.Object -} -} +}> namedType: Object.Object args: { type: { @@ -241,13 +205,10 @@ allOptional: true } } fooBarUnion: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: Union.FooBarUnion -} -} +}> namedType: Union.FooBarUnion args: null } @@ -255,13 +216,10 @@ args: null * Query enum field documentation. */ abcEnum: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: Enum.ABCEnum -} -} +}> namedType: Enum.ABCEnum args: null } @@ -305,13 +263,10 @@ export interface Interface { __interfacename: "Interface" type: { id: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["ID"] -} -} +}> namedType: $.Scalars["ID"] args: null } @@ -330,214 +285,133 @@ export namespace Object { * Object documentation. */ export interface Foo { -__typename: { -type: { -kind: "literal" -value: "Foo" -} -args: null -namedType: "Foo" -} +__typename: __typename<"Foo"> /** * Field documentation. * * @deprecated Field a is deprecated. */ id: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["ID"] -} -} +}> namedType: $.Scalars["ID"] args: null } } export interface Bar { -__typename: { -type: { -kind: "literal" -value: "Bar" -} -args: null -namedType: "Bar" -} +__typename: __typename<"Bar"> int: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["Int"] -} -} +}> namedType: $.Scalars["Int"] args: null } } export interface ObjectNested { -__typename: { -type: { -kind: "literal" -value: "ObjectNested" -} -args: null -namedType: "ObjectNested" -} +__typename: __typename<"ObjectNested"> id: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["ID"] -} -} +}> namedType: $.Scalars["ID"] args: null } object: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: Object.Object -} -} +}> namedType: Object.Object args: null } } export interface Object { -__typename: { -type: { -kind: "literal" -value: "Object" -} -args: null -namedType: "Object" -} +__typename: __typename<"Object"> string: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["String"] -} -} +}> namedType: $.Scalars["String"] args: null } int: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["Int"] -} -} +}> namedType: $.Scalars["Int"] args: null } float: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["Float"] -} -} +}> namedType: $.Scalars["Float"] args: null } boolean: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["Boolean"] -} -} +}> namedType: $.Scalars["Boolean"] args: null } id: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["ID"] -} -} +}> namedType: $.Scalars["ID"] args: null } } export interface Object1ImplementingInterface { -__typename: { -type: { -kind: "literal" -value: "Object1ImplementingInterface" -} -args: null -namedType: "Object1ImplementingInterface" -} +__typename: __typename<"Object1ImplementingInterface"> id: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["ID"] -} -} +}> namedType: $.Scalars["ID"] args: null } int: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["Int"] -} -} +}> namedType: $.Scalars["Int"] args: null } } export interface Object2ImplementingInterface { -__typename: { -type: { -kind: "literal" -value: "Object2ImplementingInterface" -} -args: null -namedType: "Object2ImplementingInterface" -} +__typename: __typename<"Object2ImplementingInterface"> id: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["ID"] -} -} +}> namedType: $.Scalars["ID"] args: null } boolean: { -type: { -kind: "nullable" -type: { +type: Nullable<{ kind: "named" named: $.Scalars["Boolean"] -} -} +}> namedType: $.Scalars["Boolean"] args: null } From 2aeadddb3182bf8eb1194b7a75a8748ef1448bb0 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Wed, 13 Mar 2024 00:50:11 -0400 Subject: [PATCH 60/71] progress --- src/Schema/schema/Field/Field.ts | 10 ++++++-- src/Schema/schema/Field/Type.ts | 2 +- src/Schema/schema/Index.ts | 2 +- src/SelectionSet/SelectionSet.test-d.ts | 14 +++++++++++ src/SelectionSet/SelectionSet.ts | 33 ++++++++++++++++++------- src/generator/generator.ts | 12 ++++----- tests/builder/_/schema.ts | 6 ----- 7 files changed, 54 insertions(+), 25 deletions(-) diff --git a/src/Schema/schema/Field/Field.ts b/src/Schema/schema/Field/Field.ts index 379b14413..4cd7fee04 100644 --- a/src/Schema/schema/Field/Field.ts +++ b/src/Schema/schema/Field/Field.ts @@ -1,9 +1,12 @@ +import type { NamedType } from '../NamedType/__.js' import type { Scalar } from '../NamedType/Scalar/_.js' import type { Any, List, Named, Nullable, Unwrap } from './Type.js' import { unwrap } from './Type.js' export type As = T extends Field ? T : never +export type Enum<$Args extends Args | null = null> = Field, $Args> + export type Scalar<$Args extends Args | null = null> = Field, $Args> export type String<$Args extends Args | null = null> = Field, $Args> @@ -14,8 +17,11 @@ export type Boolean<$Args extends Args | null = null> = Field | Nullable -export interface Args<$Fields extends Record = Record> { - allOptional: false // todo +type x = Exclude<1, 1> + +// export interface Args<$Fields extends Record = Record> { +export interface Args<$Fields extends any = any> { + allOptional: Exclude<$Fields[keyof $Fields], Nullable> extends never ? true : false fields: $Fields } diff --git a/src/Schema/schema/Field/Type.ts b/src/Schema/schema/Field/Type.ts index 9ddaf8b93..4859d6a88 100644 --- a/src/Schema/schema/Field/Type.ts +++ b/src/Schema/schema/Field/Type.ts @@ -30,7 +30,7 @@ export const named = <$Type extends NamedType.Any>(type: $Type): Named<$Type> => export type Unwrap<$Type extends Any> = $Type extends Named ? $Type['type'] : $Type extends __typename ? $Type['type'] : - Unwrap<$Type['type']> + Unwrap<$Type['type']> export const unwrap = <$Type extends Any>(type: $Type): Unwrap<$Type> => { // @ts-expect-error fixme diff --git a/src/Schema/schema/Index.ts b/src/Schema/schema/Index.ts index fa5fa22f4..f75c3f4d7 100644 --- a/src/Schema/schema/Index.ts +++ b/src/Schema/schema/Index.ts @@ -13,5 +13,5 @@ export interface Index { Union: null | Union } scalars: object - unionMemberNames: Record + // unionMemberNames: Record } diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index f7b05912c..6f169a174 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -165,6 +165,20 @@ test(`Query`, () => { }, }, }) + assertType({ + stringWithArgs: { + $: { + boolean: null, + float: null, + id: null, + int: null, + string: null, + // todo test list arg + // todo test nullable list arg + // todo test nullable list nullable arg + }, + }, + }) // all-optional + scalar + directive assertType({ stringWithArgs: { $: { boolean: true }, $skip: true } }) // builder interface diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index b4eb1d3c8..55de5298f 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -3,6 +3,7 @@ import type { MaybeList, StringNonEmpty, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../Schema/schema/__.js' +import { Unwrap } from '../Schema/schema/Field/Type.js' export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Named.Object ? Object<$Index['Root']['Query'], $Index> @@ -68,16 +69,16 @@ export type Field< > = $Field['type']['kind'] extends 'typename' ? NoArgsIndicator : // @ts-expect-error fixme? - $Field['typeUnwrapped']['kind'] extends 'Scalar' ? Indicator<$Field['typeUnwrapped']> : - $Field['typeUnwrapped']['kind'] extends 'Enum' ? Indicator<$Field['typeUnwrapped']> : + $Field['typeUnwrapped']['kind'] extends 'Scalar' ? Indicator<$Field> : + $Field['typeUnwrapped']['kind'] extends 'Enum' ? Indicator<$Field> : $Field['typeUnwrapped']['kind'] extends 'Object' ? Object<$Field['typeUnwrapped'], $Index> & FieldDirectives & Arguments<$Field> : $Field['typeUnwrapped']['kind'] extends 'Union' ? Union<$Field['typeUnwrapped'], $Index> : $Field['typeUnwrapped']['kind'] extends 'Interface' ? Interface<$Field['typeUnwrapped'], $Index> : TSError<'SelectionSetField', '$Field case not handled', { $Field: $Field }> // dprint-ignore type Arguments<$Field extends Schema.Field.Field> = -$Field['args'] extends Schema.Field.Args ? $Field['args']['allOptional'] extends true ? { $?: $Field['args']['fields'] } : - { $: $Field['args']['fields'] } : +$Field['args'] extends Schema.Field.Args ? $Field['args']['allOptional'] extends true ? { $?: Args<$Field['args']> } : + { $: Args<$Field['args']> } : {} // dprint-ignore @@ -211,11 +212,25 @@ export type OmitNegativeIndicators<$SelectionSet> = { export type NoArgsIndicator = ClientIndicator | FieldDirectives // dprint-ignore -export type Indicator<$Field extends Schema.Field.Scalar = Schema.Field.Scalar> = - $Field['args'] extends Schema.Field.Args ? $Field['args']['allOptional'] extends true - ? ({ $?: $Field['args']['fields'] } & FieldDirectives) | ClientIndicator - : { $: $Field['args']['fields'] } & FieldDirectives - : NoArgsIndicator +export type Indicator<$Field extends Schema.Field.Field = Schema.Field.Field> = +// $Field['args']['allOptional'] +$Field['args'] extends Schema.Field.Args ? $Field['args']['allOptional'] extends true + ? ({ $?: Args<$Field['args']> } & FieldDirectives) | ClientIndicator : + { $: Args<$Field['args']> } & FieldDirectives : + NoArgsIndicator + +// dprint-ignore +export type Args<$Args extends Schema.Field.Args> = +& { + [ + Key in keyof $Args['fields'] as $Args['fields'][Key] extends Schema.Field.Nullable ? never : Key + ]: ReturnType<$Args['fields'][Key]['constructor']> +} +& { + [ + Key in keyof $Args['fields'] as $Args['fields'][Key] extends Schema.Field.Nullable ? Key : never + ]?: null | ReturnType<$Args['fields'][Key]['constructor']> +} /** * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 19d3add8c..98c790ba2 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -357,7 +357,7 @@ export const generateCode = (input: Input) => { let code = `` - code += `import type * as _ from '../../../src/Schema/schema2/__.js'\n\n` + code += `import type * as _ from '../../../src/Schema/schema/__.js'\n\n` code += Code.export$( Code.namespace( @@ -377,11 +377,11 @@ export const generateCode = (input: Input) => { objects: Code.objectFromEntries( typeMapByKind.GraphQLObjectType.map(_ => [_.name, Code.propertyAccess(`Object`, _.name)]), ), - unionMemberNames: Code.objectFromEntries( - typeMapByKind.GraphQLUnionType.map( - (_) => [_.name, Code.unionItems(_.getTypes().map(_ => Code.quote(_.name)))], - ), - ), + // unionMemberNames: Code.objectFromEntries( + // typeMapByKind.GraphQLUnionType.map( + // (_) => [_.name, Code.unionItems(_.getTypes().map(_ => Code.quote(_.name)))], + // ), + // ), unions: { type: Code.objectFrom( { diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 801662a12..18f356006 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -15,11 +15,6 @@ export namespace $ { Object1ImplementingInterface: Object.Object1ImplementingInterface Object2ImplementingInterface: Object.Object2ImplementingInterface } - unionMemberNames: { - FooBarUnion: - | 'Foo' - | 'Bar' - } unions: { Union: Union.FooBarUnion } @@ -83,7 +78,6 @@ export namespace Root { */ abcEnum: _.Field<_.Nullable<_.Named>> }> - type x = Query['fields']['fooBarUnion']['typeUnwrapped']['members'][number]['fields']['__typename']['typeUnwrapped'] } // ------------------------------------------------------------ // From b69c654697ab5785ea134002cb18cfc3dc07bbcc Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Wed, 13 Mar 2024 11:43:30 -0400 Subject: [PATCH 61/71] list fixes --- src/Schema/schema/Field/Field.ts | 31 +++++++++++--------- src/SelectionSet/SelectionSet.test-d.ts | 39 +++++++++---------------- src/SelectionSet/SelectionSet.ts | 17 ++++++++--- src/generator/generator.ts | 2 +- tests/builder/_/schema.graphql | 2 ++ tests/builder/_/schema.ts | 6 ++++ 6 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/Schema/schema/Field/Field.ts b/src/Schema/schema/Field/Field.ts index 4cd7fee04..3863254c9 100644 --- a/src/Schema/schema/Field/Field.ts +++ b/src/Schema/schema/Field/Field.ts @@ -1,35 +1,38 @@ import type { NamedType } from '../NamedType/__.js' import type { Scalar } from '../NamedType/Scalar/_.js' -import type { Any, List, Named, Nullable, Unwrap } from './Type.js' +import type * as Type from './Type.js' import { unwrap } from './Type.js' export type As = T extends Field ? T : never -export type Enum<$Args extends Args | null = null> = Field, $Args> +export type Enum<$Args extends Args | null = null> = Field, $Args> -export type Scalar<$Args extends Args | null = null> = Field, $Args> +export type Scalar<$Args extends Args | null = null> = Field, $Args> -export type String<$Args extends Args | null = null> = Field, $Args> +export type String<$Args extends Args | null = null> = Field, $Args> -export type Number<$Args extends Args | null = null> = Field, $Args> +export type Number<$Args extends Args | null = null> = Field, $Args> -export type Boolean<$Args extends Args | null = null> = Field, $Args> +export type Boolean<$Args extends Args | null = null> = Field, $Args> -type InputFieldType = Scalar.Any | List | Nullable - -type x = Exclude<1, 1> +export namespace Input { + export type Nullable = Type.Nullable + export type List = Type.List + export type Any = Scalar.Any | List | Nullable +} // export interface Args<$Fields extends Record = Record> { export interface Args<$Fields extends any = any> { - allOptional: Exclude<$Fields[keyof $Fields], Nullable> extends never ? true : false + allOptional: Exclude<$Fields[keyof $Fields], Type.Nullable> extends never ? true : false fields: $Fields } -export const field = <$Type extends Any, $Args extends null | Args = null>( +export const field = <$Type extends Type.Any, $Args extends null | Args = null>( type: $Type, args: $Args = null as $Args, ): Field<$Type, $Args> => { - // @ts-expect-error fixme + // eslint-disable-next-line + // @ts-ignore infinite depth issue, can this be fixed? return { typeUnwrapped: unwrap(type), type, @@ -37,8 +40,8 @@ export const field = <$Type extends Any, $Args extends null | Args = null>( } } -export type Field<$Type extends Any = Any, $Args extends Args | null = Args | null> = { - typeUnwrapped: Unwrap<$Type> +export type Field<$Type extends Type.Any = Type.Any, $Args extends Args | null = Args | null> = { + typeUnwrapped: Type.Unwrap<$Type> type: $Type args: $Args } diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 6f169a174..e830f9dff 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -154,31 +154,20 @@ test(`Query`, () => { // all-optional on scalar assertType({ stringWithArgs: true }) assertType({ stringWithArgs: {} }) - assertType({ - stringWithArgs: { - $: { - boolean: true, - float: 1, - id: `id`, - int: 3, - string: `abc`, - }, - }, - }) - assertType({ - stringWithArgs: { - $: { - boolean: null, - float: null, - id: null, - int: null, - string: null, - // todo test list arg - // todo test nullable list arg - // todo test nullable list nullable arg - }, - }, - }) + assertType({ stringWithArgs: { $: { boolean: true, float: 1, id: `id`, int: 3, string: `abc` } } }) + assertType({ stringWithArgs: { $: { boolean: null, float: null, id: null, int: null, string: null } } }) + + // input list + assertType({ stringWithListArg: { $: { ints: [1, 2, 3] } } }) + assertType({ stringWithListArg: { $: { ints: [] } } }) + assertType({ stringWithListArg: { $: { ints: [null] } } }) + assertType({ stringWithListArg: { $: { ints: null } } }) + assertType({ stringWithListArg: { $: {} } }) + // @ts-expect-error missing "ints" arg + assertType({ stringWithListArgRequired: { $: {} } }) + // @ts-expect-error missing non-null "ints" arg + assertType({ stringWithListArgRequired: { $: { ints: null } } }) + // all-optional + scalar + directive assertType({ stringWithArgs: { $: { boolean: true }, $skip: true } }) // builder interface diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 55de5298f..1cabfa611 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -3,7 +3,6 @@ import type { MaybeList, StringNonEmpty, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../Schema/schema/__.js' -import { Unwrap } from '../Schema/schema/Field/Type.js' export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Named.Object ? Object<$Index['Root']['Query'], $Index> @@ -68,7 +67,8 @@ export type Field< $Index extends Schema.Index, > = $Field['type']['kind'] extends 'typename' ? NoArgsIndicator : - // @ts-expect-error fixme? + // eslint-disable-next-line + // @ts-ignore infinite depth issue, can this be fixed? $Field['typeUnwrapped']['kind'] extends 'Scalar' ? Indicator<$Field> : $Field['typeUnwrapped']['kind'] extends 'Enum' ? Indicator<$Field> : $Field['typeUnwrapped']['kind'] extends 'Object' ? Object<$Field['typeUnwrapped'], $Index> & FieldDirectives & Arguments<$Field> : @@ -224,14 +224,23 @@ export type Args<$Args extends Schema.Field.Args> = & { [ Key in keyof $Args['fields'] as $Args['fields'][Key] extends Schema.Field.Nullable ? never : Key - ]: ReturnType<$Args['fields'][Key]['constructor']> + ]: InferTypeInput<$Args['fields'][Key]> } & { [ Key in keyof $Args['fields'] as $Args['fields'][Key] extends Schema.Field.Nullable ? Key : never - ]?: null | ReturnType<$Args['fields'][Key]['constructor']> + ]?: null | InferTypeInput<$Args['fields'][Key]> } +// todo input objects +// todo enums +// dprint-ignore +type InferTypeInput<$InputType extends Schema.Field.Input.Any> = +$InputType extends Schema.Field.Input.Nullable ? InferTypeInput<$InputType['type']> | null : +$InputType extends Schema.Field.Input.List ? InferTypeInput<$InputType['type']>[] : +$InputType extends Schema.Named.Scalar.Any ? ReturnType<$InputType['constructor']> : + TSError<'U', 'Unknown $InputType', { $InputType: $InputType }> // never + /** * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives */ diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 98c790ba2..16bcd66d5 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -110,7 +110,7 @@ const pointerRenderers = defineReferenceRenderers({ GraphQLEnumType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLEnumType, node.name), GraphQLInputObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInputObjectType, node.name), GraphQLInterfaceType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), - GraphQLList: (config, node) => Code.list(dispatchToReferenceRenderer(config, node.ofType)), + GraphQLList: (config, node) => `_.List<${(dispatchToReferenceRenderer(config, node.ofType))}>`, GraphQLObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), GraphQLScalarType: (_, node) => `_.Scalar.${node.name}`, GraphQLUnionType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index 665a13a11..7f4f87aed 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -5,6 +5,8 @@ type Query { string: String stringWithRequiredArg(string:String!): String stringWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): String + stringWithListArg(ints:[Int]): String + stringWithListArgRequired(ints:[Int]!): String object: Object listListIntNonNull: [[Int!]!]! listListInt: [[Int]] diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 18f356006..29caab13a 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -55,6 +55,12 @@ export namespace Root { id: _.Nullable<_.Scalar.ID> }> > + stringWithListArg: _.Field< + _.Nullable<_.Named<_.Scalar.String>>, + _.Args<{ + ints: _.Nullable<_.List<_.Scalar.Int>> + }> + > object: _.Field<_.Nullable<_.Named>> listListIntNonNull: _.Field<_.List<_.List<_.Named<_.Scalar.Int>>>> listListInt: _.Field<_.Nullable<_.List<_.Nullable<_.List<_.Nullable<_.Named<_.Scalar.Int>>>>>>> From 441651cf30c25639324b7d8a58c25e174744c186 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Thu, 14 Mar 2024 21:54:18 -0400 Subject: [PATCH 62/71] work --- src/Schema/schema/Field/Field.ts | 20 ++++--- src/Schema/schema/Field/Type.ts | 21 ++++--- src/SelectionSet/SelectionSet.test-d.ts | 13 +++++ src/SelectionSet/SelectionSet.ts | 12 ++-- src/generator/generator.ts | 35 +++++++----- src/lib/graphql.ts | 11 ++++ tests/builder/_/schema.graphql | 1 + tests/builder/_/schema.ts | 76 ++++++++++++++----------- 8 files changed, 117 insertions(+), 72 deletions(-) diff --git a/src/Schema/schema/Field/Field.ts b/src/Schema/schema/Field/Field.ts index 3863254c9..37ebe7d1d 100644 --- a/src/Schema/schema/Field/Field.ts +++ b/src/Schema/schema/Field/Field.ts @@ -3,21 +3,23 @@ import type { Scalar } from '../NamedType/Scalar/_.js' import type * as Type from './Type.js' import { unwrap } from './Type.js' +export type * as Type from './Type.js' + export type As = T extends Field ? T : never -export type Enum<$Args extends Args | null = null> = Field, $Args> +export type Enum<$Args extends Args | null = null> = Field -export type Scalar<$Args extends Args | null = null> = Field, $Args> +export type Scalar<$Args extends Args | null = null> = Field -export type String<$Args extends Args | null = null> = Field, $Args> +export type String<$Args extends Args | null = null> = Field -export type Number<$Args extends Args | null = null> = Field, $Args> +export type Number<$Args extends Args | null = null> = Field -export type Boolean<$Args extends Args | null = null> = Field, $Args> +export type Boolean<$Args extends Args | null = null> = Field export namespace Input { - export type Nullable = Type.Nullable - export type List = Type.List + export type Nullable = Type.Nullable + export type List = Type.List export type Any = Scalar.Any | List | Nullable } @@ -31,9 +33,9 @@ export const field = <$Type extends Type.Any, $Args extends null | Args = null>( type: $Type, args: $Args = null as $Args, ): Field<$Type, $Args> => { - // eslint-disable-next-line - // @ts-ignore infinite depth issue, can this be fixed? return { + // eslint-disable-next-line + // @ts-ignore infinite depth issue, can this be fixed? typeUnwrapped: unwrap(type), type, args, diff --git a/src/Schema/schema/Field/Type.ts b/src/Schema/schema/Field/Type.ts index 4859d6a88..fa365d923 100644 --- a/src/Schema/schema/Field/Type.ts +++ b/src/Schema/schema/Field/Type.ts @@ -1,22 +1,20 @@ +import type { TSError } from '../../../lib/TSError.js' import type { NamedType } from '../NamedType/__.js' export interface __typename<$Type extends string = string> { kind: 'typename' type: $Type } -export interface Nullable<$Type> { +export interface Nullable<$Type extends Any> { kind: 'nullable' type: $Type } -export interface List<$Type> { +export interface List<$Type extends Any> { kind: 'list' type: $Type } -export interface Named<$Type extends NamedType.Any = NamedType.Any> { - kind: 'named' - type: $Type -} -export type Any = List | __typename | Nullable | Named + +export type Any = List | __typename | Nullable | NamedType.Any export const __typename = <$Type extends string>(type: $Type): __typename<$Type> => ({ kind: `typename`, type }) export const nullable = <$Type extends __typename | List>(type: $Type): Nullable<$Type> => ({ @@ -24,13 +22,14 @@ export const nullable = <$Type extends __typename | List>(type: $Type) type, }) export const list = <$Type extends Any>(type: $Type): List<$Type> => ({ kind: `list`, type }) -export const named = <$Type extends NamedType.Any>(type: $Type): Named<$Type> => ({ kind: `named`, type }) // dprint-ignore export type Unwrap<$Type extends Any> = - $Type extends Named ? $Type['type'] : - $Type extends __typename ? $Type['type'] : - Unwrap<$Type['type']> + $Type extends List ? Unwrap<$innerType> : + $Type extends Nullable ? Unwrap<$innerType> : + $Type extends __typename ? $Type['type'] : + $Type extends NamedType.Any ? $Type : + TSError<'Unwrap', 'Unknown $Type', { $Type: $Type }> export const unwrap = <$Type extends Any>(type: $Type): Unwrap<$Type> => { // @ts-expect-error fixme diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index e830f9dff..112bafb52 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -157,6 +157,19 @@ test(`Query`, () => { assertType({ stringWithArgs: { $: { boolean: true, float: 1, id: `id`, int: 3, string: `abc` } } }) assertType({ stringWithArgs: { $: { boolean: null, float: null, id: null, int: null, string: null } } }) + // enum arg + assertType({ stringWithArgEnum: { $: { ABCEnum: `A` } } }) + assertType({ stringWithArgEnum: { $: { ABCEnum: `B` } } }) + assertType({ stringWithArgEnum: { $: { ABCEnum: `C` } } }) + assertType({ stringWithArgEnum: { $: { ABCEnum: null } } }) + assertType({ stringWithArgEnum: { $: {} } }) + // @ts-expect-error invalid enum value + assertType({ stringWithArgEnum: { $: { ABCEnum: `D` } } }) + // @ts-expect-error invalid enum value + assertType({ stringWithArgEnum: { $: { ABCEnum: `` } } }) + // @ts-expect-error invalid enum value + assertType({ stringWithArgEnum: { $: { ABCEnum: 1 } } }) + // input list assertType({ stringWithListArg: { $: { ints: [1, 2, 3] } } }) assertType({ stringWithListArg: { $: { ints: [] } } }) diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 1cabfa611..a26776827 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -30,6 +30,8 @@ type Fields< & { [Key in keyof $Fields]?: + // eslint-disable-next-line + // @ts-ignore excessive deep error, fixme? Field, $Index> } & @@ -233,13 +235,13 @@ export type Args<$Args extends Schema.Field.Args> = } // todo input objects -// todo enums // dprint-ignore type InferTypeInput<$InputType extends Schema.Field.Input.Any> = -$InputType extends Schema.Field.Input.Nullable ? InferTypeInput<$InputType['type']> | null : -$InputType extends Schema.Field.Input.List ? InferTypeInput<$InputType['type']>[] : -$InputType extends Schema.Named.Scalar.Any ? ReturnType<$InputType['constructor']> : - TSError<'U', 'Unknown $InputType', { $InputType: $InputType }> // never + $InputType extends Schema.Field.Input.Nullable ? InferTypeInput<$InputType['type']> | null : + $InputType extends Schema.Field.Input.List ? InferTypeInput<$InputType['type']>[] : + $InputType extends Schema.Named.Enum ? $Members[number] : + $InputType extends Schema.Named.Scalar.Any ? ReturnType<$InputType['constructor']> : + TSError<'InferTypeInput', 'Unknown $InputType', { $InputType: $InputType }> // never /** * @see https://spec.graphql.org/draft/#sec-Type-System.Directives.Built-in-Directives diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 16bcd66d5..7e2f2cf50 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -16,6 +16,7 @@ import type { AnyClass, AnyField, AnyNamedClassName, + ClassToName, Describable, NameToClassNamedType, TypeMapByKind, @@ -84,15 +85,29 @@ const defineConcreteRenderers = < ) as any } -const dispatchToReferenceRenderer = (config: Config, node: AnyClass): string => { +const dispatchToReferenceRenderer = (config: Config, node: AnyClass): string => + getReferenceRenderer(node)(config, node as any) + +const getReferenceRenderer = (node: N): (typeof referenceRenderers)[ClassToName] => { // @ts-expect-error lookup - const renderer = pointerRenderers[node.constructor.name] // eslint-disable-line + const renderer = referenceRenderers[node.constructor.name] // eslint-disable-line if (!renderer) { throw new Error(`No renderer found for class: ${node.constructor.name}`) } - return renderer(config, node) // eslint-disable-line + return renderer } +const referenceRenderers = defineReferenceRenderers({ + GraphQLNonNull: (config, node) => dispatchToReferenceRenderer(config, node.ofType), + GraphQLEnumType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLEnumType, node.name), + GraphQLInputObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInputObjectType, node.name), + GraphQLInterfaceType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), + GraphQLObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), + GraphQLUnionType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), + GraphQLList: (config, node) => `_.List<${(buildType(config, node.ofType))}>`, + GraphQLScalarType: (_, node) => `_.Scalar.${node.name}`, +}) + const dispatchToConcreteRenderer = ( config: Config, node: GraphQLNamedType, @@ -105,17 +120,6 @@ const dispatchToConcreteRenderer = ( return renderer(config, node) // eslint-disable-line } -const pointerRenderers = defineReferenceRenderers({ - GraphQLNonNull: (config, node) => dispatchToReferenceRenderer(config, node.ofType), - GraphQLEnumType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLEnumType, node.name), - GraphQLInputObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInputObjectType, node.name), - GraphQLInterfaceType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLInterfaceType, node.name), - GraphQLList: (config, node) => `_.List<${(dispatchToReferenceRenderer(config, node.ofType))}>`, - GraphQLObjectType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLObjectType, node.name), - GraphQLScalarType: (_, node) => `_.Scalar.${node.name}`, - GraphQLUnionType: (_, node) => Code.propertyAccess(namespaceNames.GraphQLUnionType, node.name), -}) - const concreteRenderers = defineConcreteRenderers({ GraphQLEnumType: (config, node) => Code.TSDoc( @@ -238,7 +242,8 @@ const buildType = (config: Config, node: AnyClass) => { if (isNamedType(nodeInner)) { const namedTypeReference = dispatchToReferenceRenderer(config, nodeInner) - const namedTypeCode = `_.Named<${namedTypeReference}>` + // const namedTypeCode = `_.Named<${namedTypeReference}>` + const namedTypeCode = namedTypeReference return nullable ? `_.Nullable<${namedTypeCode}>` : namedTypeCode diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index 31efb8d3b..67a29cc66 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -61,6 +61,17 @@ export const getTypeMapByKind = (schema: GraphQLSchema) => { return typeMapByKind } +export type ClassToName = + C extends GraphQLScalarType ? `GraphQLScalarType` + : C extends GraphQLObjectType ? `GraphQLObjectType` + : C extends GraphQLInterfaceType ? `GraphQLInterfaceType` + : C extends GraphQLUnionType ? `GraphQLUnionType` + : C extends GraphQLEnumType ? `GraphQLEnumType` + : C extends GraphQLInputObjectType ? `GraphQLInputObjectType` + : C extends GraphQLList ? `GraphQLList` + : C extends GraphQLNonNull ? `GraphQLNonNull` + : never + export const NameToClassNamedType = { GraphQLScalarType: GraphQLScalarType, GraphQLObjectType: GraphQLObjectType, diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index 7f4f87aed..289a7e895 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -5,6 +5,7 @@ type Query { string: String stringWithRequiredArg(string:String!): String stringWithArgs(string:String, int:Int, float:Float, boolean:Boolean, id:ID): String + stringWithArgEnum(ABCEnum:ABCEnum): String stringWithListArg(ints:[Int]): String stringWithListArgRequired(ints:[Int]!): String object: Object diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 29caab13a..f8a97cf0a 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -35,18 +35,18 @@ export namespace $ { export namespace Root { export type Query = _.Object<'Query', { - interface: _.Field<_.Nullable<_.Named>> - id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> - idNonNull: _.Field<_.Named<_.Scalar.ID>> - string: _.Field<_.Nullable<_.Named<_.Scalar.String>>> + interface: _.Field<_.Nullable> + id: _.Field<_.Nullable<_.Scalar.ID>> + idNonNull: _.Field<_.Scalar.ID> + string: _.Field<_.Nullable<_.Scalar.String>> stringWithRequiredArg: _.Field< - _.Nullable<_.Named<_.Scalar.String>>, + _.Nullable<_.Scalar.String>, _.Args<{ string: _.Scalar.String }> > stringWithArgs: _.Field< - _.Nullable<_.Named<_.Scalar.String>>, + _.Nullable<_.Scalar.String>, _.Args<{ string: _.Nullable<_.Scalar.String> int: _.Nullable<_.Scalar.Int> @@ -55,21 +55,33 @@ export namespace Root { id: _.Nullable<_.Scalar.ID> }> > + stringWithArgEnum: _.Field< + _.Nullable<_.Scalar.String>, + _.Args<{ + ABCEnum: _.Nullable + }> + > stringWithListArg: _.Field< - _.Nullable<_.Named<_.Scalar.String>>, + _.Nullable<_.Scalar.String>, + _.Args<{ + ints: _.Nullable<_.List<_.Nullable<_.Scalar.Int>>> + }> + > + stringWithListArgRequired: _.Field< + _.Nullable<_.Scalar.String>, _.Args<{ - ints: _.Nullable<_.List<_.Scalar.Int>> + ints: _.List<_.Nullable<_.Scalar.Int>> }> > - object: _.Field<_.Nullable<_.Named>> - listListIntNonNull: _.Field<_.List<_.List<_.Named<_.Scalar.Int>>>> - listListInt: _.Field<_.Nullable<_.List<_.Nullable<_.List<_.Nullable<_.Named<_.Scalar.Int>>>>>>> - listInt: _.Field<_.Nullable<_.List<_.Nullable<_.Named<_.Scalar.Int>>>>> - listIntNonNull: _.Field<_.List<_.Named<_.Scalar.Int>>> - objectNested: _.Field<_.Nullable<_.Named>> - objectNonNull: _.Field<_.Named> + object: _.Field<_.Nullable> + listListIntNonNull: _.Field<_.List<_.List<_.Scalar.Int>>> + listListInt: _.Field<_.Nullable<_.List<_.Nullable<_.List<_.Nullable<_.Scalar.Int>>>>>> + listInt: _.Field<_.Nullable<_.List<_.Nullable<_.Scalar.Int>>>> + listIntNonNull: _.Field<_.List<_.Scalar.Int>> + objectNested: _.Field<_.Nullable> + objectNonNull: _.Field objectWithArgs: _.Field< - _.Nullable<_.Named>, + _.Nullable, _.Args<{ string: _.Nullable<_.Scalar.String> int: _.Nullable<_.Scalar.Int> @@ -78,11 +90,11 @@ export namespace Root { id: _.Nullable<_.Scalar.ID> }> > - fooBarUnion: _.Field<_.Nullable<_.Named>> + fooBarUnion: _.Field<_.Nullable> /** * Query enum field documentation. */ - abcEnum: _.Field<_.Nullable<_.Named>> + abcEnum: _.Field<_.Nullable> }> } @@ -116,7 +128,7 @@ export namespace InputObject { export namespace Interface { export type Interface = _.Interface<'Interface', { - id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + id: _.Field<_.Nullable<_.Scalar.ID>> }, [Object.Object1ImplementingInterface, Object.Object2ImplementingInterface]> } @@ -134,34 +146,34 @@ export namespace Object { * * @deprecated Field a is deprecated. */ - id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + id: _.Field<_.Nullable<_.Scalar.ID>> }> export type Bar = _.Object<'Bar', { - int: _.Field<_.Nullable<_.Named<_.Scalar.Int>>> + int: _.Field<_.Nullable<_.Scalar.Int>> }> export type ObjectNested = _.Object<'ObjectNested', { - id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> - object: _.Field<_.Nullable<_.Named>> + id: _.Field<_.Nullable<_.Scalar.ID>> + object: _.Field<_.Nullable> }> export type Object = _.Object<'Object', { - string: _.Field<_.Nullable<_.Named<_.Scalar.String>>> - int: _.Field<_.Nullable<_.Named<_.Scalar.Int>>> - float: _.Field<_.Nullable<_.Named<_.Scalar.Float>>> - boolean: _.Field<_.Nullable<_.Named<_.Scalar.Boolean>>> - id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> + string: _.Field<_.Nullable<_.Scalar.String>> + int: _.Field<_.Nullable<_.Scalar.Int>> + float: _.Field<_.Nullable<_.Scalar.Float>> + boolean: _.Field<_.Nullable<_.Scalar.Boolean>> + id: _.Field<_.Nullable<_.Scalar.ID>> }> export type Object1ImplementingInterface = _.Object<'Object1ImplementingInterface', { - id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> - int: _.Field<_.Nullable<_.Named<_.Scalar.Int>>> + id: _.Field<_.Nullable<_.Scalar.ID>> + int: _.Field<_.Nullable<_.Scalar.Int>> }> export type Object2ImplementingInterface = _.Object<'Object2ImplementingInterface', { - id: _.Field<_.Nullable<_.Named<_.Scalar.ID>>> - boolean: _.Field<_.Nullable<_.Named<_.Scalar.Boolean>>> + id: _.Field<_.Nullable<_.Scalar.ID>> + boolean: _.Field<_.Nullable<_.Scalar.Boolean>> }> } From 99a5745b239cb0337eacb35a114d15a10a12c906 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Thu, 14 Mar 2024 21:59:23 -0400 Subject: [PATCH 63/71] tidy --- src/Schema/{schema => }/Field/Field.ts | 0 src/Schema/{schema => }/Field/Type.ts | 2 +- src/Schema/{schema => }/Field/_.ts | 0 src/Schema/{schema => }/Field/__.ts | 0 src/Schema/{schema => }/Index.ts | 1 - src/Schema/{schema => }/NamedType/Enum.ts | 0 .../{schema => }/NamedType/Interface.ts | 0 src/Schema/NamedType/NamedType.test-d.ts | 17 + .../{schema => }/NamedType/NamedType.ts | 2 +- src/Schema/{schema => }/NamedType/Object.ts | 0 .../{schema => }/NamedType/Scalar/Scalar.ts | 0 src/Schema/{schema => }/NamedType/Scalar/_.ts | 0 .../NamedType/Scalar/nativeConstructors.ts | 0 src/Schema/{schema => }/NamedType/Union.ts | 0 src/Schema/{schema => }/NamedType/_.ts | 0 src/Schema/{schema => }/NamedType/__.ts | 0 src/Schema/Schema.test-d.ts | 17 - src/Schema/Schema.ts | 68 --- src/Schema/__.ts | 6 +- .../{schema/__namespaced.ts => __Schema.ts} | 0 src/Schema/schema/__.ts | 5 - src/SelectionSet/SelectionSet.ts | 2 +- src/generator/generator.ts | 2 +- tests/builder/_/schema.ts | 2 +- .../__snapshots__/generate.test.ts.snap | 391 +++--------------- 25 files changed, 93 insertions(+), 422 deletions(-) rename src/Schema/{schema => }/Field/Field.ts (100%) rename src/Schema/{schema => }/Field/Type.ts (96%) rename src/Schema/{schema => }/Field/_.ts (100%) rename src/Schema/{schema => }/Field/__.ts (100%) rename src/Schema/{schema => }/Index.ts (87%) rename src/Schema/{schema => }/NamedType/Enum.ts (100%) rename src/Schema/{schema => }/NamedType/Interface.ts (100%) create mode 100644 src/Schema/NamedType/NamedType.test-d.ts rename src/Schema/{schema => }/NamedType/NamedType.ts (93%) rename src/Schema/{schema => }/NamedType/Object.ts (100%) rename src/Schema/{schema => }/NamedType/Scalar/Scalar.ts (100%) rename src/Schema/{schema => }/NamedType/Scalar/_.ts (100%) rename src/Schema/{schema => }/NamedType/Scalar/nativeConstructors.ts (100%) rename src/Schema/{schema => }/NamedType/Union.ts (100%) rename src/Schema/{schema => }/NamedType/_.ts (100%) rename src/Schema/{schema => }/NamedType/__.ts (100%) delete mode 100644 src/Schema/Schema.test-d.ts delete mode 100644 src/Schema/Schema.ts rename src/Schema/{schema/__namespaced.ts => __Schema.ts} (100%) delete mode 100644 src/Schema/schema/__.ts diff --git a/src/Schema/schema/Field/Field.ts b/src/Schema/Field/Field.ts similarity index 100% rename from src/Schema/schema/Field/Field.ts rename to src/Schema/Field/Field.ts diff --git a/src/Schema/schema/Field/Type.ts b/src/Schema/Field/Type.ts similarity index 96% rename from src/Schema/schema/Field/Type.ts rename to src/Schema/Field/Type.ts index fa365d923..8f64a5af6 100644 --- a/src/Schema/schema/Field/Type.ts +++ b/src/Schema/Field/Type.ts @@ -1,4 +1,4 @@ -import type { TSError } from '../../../lib/TSError.js' +import type { TSError } from '../../lib/TSError.js' import type { NamedType } from '../NamedType/__.js' export interface __typename<$Type extends string = string> { diff --git a/src/Schema/schema/Field/_.ts b/src/Schema/Field/_.ts similarity index 100% rename from src/Schema/schema/Field/_.ts rename to src/Schema/Field/_.ts diff --git a/src/Schema/schema/Field/__.ts b/src/Schema/Field/__.ts similarity index 100% rename from src/Schema/schema/Field/__.ts rename to src/Schema/Field/__.ts diff --git a/src/Schema/schema/Index.ts b/src/Schema/Index.ts similarity index 87% rename from src/Schema/schema/Index.ts rename to src/Schema/Index.ts index f75c3f4d7..99f7eff38 100644 --- a/src/Schema/schema/Index.ts +++ b/src/Schema/Index.ts @@ -13,5 +13,4 @@ export interface Index { Union: null | Union } scalars: object - // unionMemberNames: Record } diff --git a/src/Schema/schema/NamedType/Enum.ts b/src/Schema/NamedType/Enum.ts similarity index 100% rename from src/Schema/schema/NamedType/Enum.ts rename to src/Schema/NamedType/Enum.ts diff --git a/src/Schema/schema/NamedType/Interface.ts b/src/Schema/NamedType/Interface.ts similarity index 100% rename from src/Schema/schema/NamedType/Interface.ts rename to src/Schema/NamedType/Interface.ts diff --git a/src/Schema/NamedType/NamedType.test-d.ts b/src/Schema/NamedType/NamedType.test-d.ts new file mode 100644 index 000000000..df28fbcde --- /dev/null +++ b/src/Schema/NamedType/NamedType.test-d.ts @@ -0,0 +1,17 @@ +import { expectTypeOf, test } from 'vitest' +import type * as NamedType from './NamedType.js' + +test(`NameParse`, () => { + expectTypeOf>().toEqualTypeOf<'a'>() + expectTypeOf>().toEqualTypeOf<'a1'>() + expectTypeOf>().toEqualTypeOf<'A'>() + expectTypeOf>().toEqualTypeOf<'aa'>() + expectTypeOf>().toEqualTypeOf<'a_'>() + expectTypeOf>().toEqualTypeOf<'a__'>() + expectTypeOf>().toEqualTypeOf<'a__b'>() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() +}) diff --git a/src/Schema/schema/NamedType/NamedType.ts b/src/Schema/NamedType/NamedType.ts similarity index 93% rename from src/Schema/schema/NamedType/NamedType.ts rename to src/Schema/NamedType/NamedType.ts index a5d17c781..1def4305b 100644 --- a/src/Schema/schema/NamedType/NamedType.ts +++ b/src/Schema/NamedType/NamedType.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/ban-types */ -import type { Digit, Letter } from '../../../lib/prelude.js' +import type { Digit, Letter } from '../../lib/prelude.js' import type { Enum } from './Enum.js' import type { Interface } from './Interface.js' import type { Object } from './Object.js' diff --git a/src/Schema/schema/NamedType/Object.ts b/src/Schema/NamedType/Object.ts similarity index 100% rename from src/Schema/schema/NamedType/Object.ts rename to src/Schema/NamedType/Object.ts diff --git a/src/Schema/schema/NamedType/Scalar/Scalar.ts b/src/Schema/NamedType/Scalar/Scalar.ts similarity index 100% rename from src/Schema/schema/NamedType/Scalar/Scalar.ts rename to src/Schema/NamedType/Scalar/Scalar.ts diff --git a/src/Schema/schema/NamedType/Scalar/_.ts b/src/Schema/NamedType/Scalar/_.ts similarity index 100% rename from src/Schema/schema/NamedType/Scalar/_.ts rename to src/Schema/NamedType/Scalar/_.ts diff --git a/src/Schema/schema/NamedType/Scalar/nativeConstructors.ts b/src/Schema/NamedType/Scalar/nativeConstructors.ts similarity index 100% rename from src/Schema/schema/NamedType/Scalar/nativeConstructors.ts rename to src/Schema/NamedType/Scalar/nativeConstructors.ts diff --git a/src/Schema/schema/NamedType/Union.ts b/src/Schema/NamedType/Union.ts similarity index 100% rename from src/Schema/schema/NamedType/Union.ts rename to src/Schema/NamedType/Union.ts diff --git a/src/Schema/schema/NamedType/_.ts b/src/Schema/NamedType/_.ts similarity index 100% rename from src/Schema/schema/NamedType/_.ts rename to src/Schema/NamedType/_.ts diff --git a/src/Schema/schema/NamedType/__.ts b/src/Schema/NamedType/__.ts similarity index 100% rename from src/Schema/schema/NamedType/__.ts rename to src/Schema/NamedType/__.ts diff --git a/src/Schema/Schema.test-d.ts b/src/Schema/Schema.test-d.ts deleted file mode 100644 index ed2c2a83a..000000000 --- a/src/Schema/Schema.test-d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { expectTypeOf, test } from 'vitest' -import type { Schema } from './__.js' - -test(`NameParse`, () => { - expectTypeOf>().toEqualTypeOf<'a'>() - expectTypeOf>().toEqualTypeOf<'a1'>() - expectTypeOf>().toEqualTypeOf<'A'>() - expectTypeOf>().toEqualTypeOf<'aa'>() - expectTypeOf>().toEqualTypeOf<'a_'>() - expectTypeOf>().toEqualTypeOf<'a__'>() - expectTypeOf>().toEqualTypeOf<'a__b'>() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() - expectTypeOf>().toEqualTypeOf() -}) diff --git a/src/Schema/Schema.ts b/src/Schema/Schema.ts deleted file mode 100644 index a88b3cc56..000000000 --- a/src/Schema/Schema.ts +++ /dev/null @@ -1,68 +0,0 @@ -// /* eslint-disable @typescript-eslint/ban-types */ - -// import type { Digit, Letter } from '../lib/prelude.js' - -// export interface Index { -// Root: { -// Query: null | Object -// Mutation: null | Object -// Subscription: null | Object -// } -// objects: Record -// unions: { -// Union: null | Union -// } -// scalars: object -// unionMemberNames: Record -// } - -// // export type Nullable = null -// // todo needs to be extensible for custom scalars... -// export type Scalar = string | boolean | number // Schema.$.Scalars[keyof Schema.$.Scalars] -// export type Object = { __typename: Schema2.Field.Type.Typename } -// export type Union = { __unionname: string; type: Object } -// export type Interface$ = { __interfacename: string; type: object; implementors: Object } -// export type Literal = string -// export type Named = Scalar | Object | Union | Literal | Interface$ -// export type Node = Object | Union | Scalar // | Nullable - -// export type FieldTypeNamed = { kind: 'named'; named: any } -// export type FieldTypeLiteral = { kind: 'literal'; value: string } -// export type FieldTypeList = { kind: 'list'; type: any } -// export type FieldTypeNullable = { kind: 'nullable'; type: any } -// export type FieldType = FieldTypeNamed | FieldTypeList | FieldTypeNullable | FieldTypeLiteral - -// export type Field<$Named extends Named = Named> = { -// args: null | FieldArgs -// type: FieldType -// namedType: $Named -// } -// export type FieldScalar = Field -// export type FieldObject = Field -// export type FieldUnion = Field -// export type FieldInterface = Field -// export type FieldTypename = { args: null; type: FieldTypeLiteral; namedType: string } - -// export type FieldArgs = { type: object; allOptional: boolean } - -// export type AsField = T extends Field ? T : never - -// /** -// * @see http://spec.graphql.org/draft/#sec-Names -// */ -// // dprint-ignore -// export type NameParse = -// T extends NameHead ? T : -// T extends `${NameHead}${infer Rest}` ? Rest extends NameBodyParse ? T -// : never -// : never - -// // dprint-ignore -// export type NameBodyParse = -// S extends NameBody ? S : -// S extends `${NameBody}${infer Rest}` ? NameBodyParse extends string ? S -// : never -// : never - -// export type NameHead = Letter | '_' -// export type NameBody = Letter | '_' | Digit diff --git a/src/Schema/__.ts b/src/Schema/__.ts index 909fa2233..5fea0ca18 100644 --- a/src/Schema/__.ts +++ b/src/Schema/__.ts @@ -1 +1,5 @@ -export * as Schema from './Schema.js' +export * as Schema from './__namespaced.js' +export { Args, As, Field } from './Field/__.js' +export { __typename, List, Nullable } from './Field/Type.js' +export { Index } from './Index.js' +export * from './NamedType/_.js' diff --git a/src/Schema/schema/__namespaced.ts b/src/Schema/__Schema.ts similarity index 100% rename from src/Schema/schema/__namespaced.ts rename to src/Schema/__Schema.ts diff --git a/src/Schema/schema/__.ts b/src/Schema/schema/__.ts deleted file mode 100644 index d41375060..000000000 --- a/src/Schema/schema/__.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * as Schema from './__namespaced.js' -export { Args, As, Field } from './Field/__.js' -export { __typename, List, Named, Nullable } from './Field/Type.js' -export { Index } from './Index.js' -export * from './NamedType/_.js' diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index a26776827..91d60f369 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -2,7 +2,7 @@ import type { MaybeList, StringNonEmpty, Values } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' -import type { Schema } from '../Schema/schema/__.js' +import type { Schema } from '../Schema/__.js' export type Query<$Index extends Schema.Index> = $Index['Root']['Query'] extends Schema.Named.Object ? Object<$Index['Root']['Query'], $Index> diff --git a/src/generator/generator.ts b/src/generator/generator.ts index 7e2f2cf50..5fec88177 100644 --- a/src/generator/generator.ts +++ b/src/generator/generator.ts @@ -362,7 +362,7 @@ export const generateCode = (input: Input) => { let code = `` - code += `import type * as _ from '../../../src/Schema/schema/__.js'\n\n` + code += `import type * as _ from '../../../src/Schema/__.js'\n\n` code += Code.export$( Code.namespace( diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index f8a97cf0a..92244d66b 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -1,4 +1,4 @@ -import type * as _ from '../../../src/Schema/schema/__.js' +import type * as _ from '../../../src/Schema/__.js' export namespace $ { export interface Index { diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index a34877c57..d1d1df790 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -1,7 +1,8 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`generates types from GraphQL SDL file 1`] = ` -"import { __typename, Nullable } from '../../../src/Schema/schema2.js' +"import type * as _ from '../../../src/Schema/__.js' + export namespace $ { export interface Index { Root: { @@ -17,10 +18,6 @@ Object: Object.Object Object1ImplementingInterface: Object.Object1ImplementingInterface Object2ImplementingInterface: Object.Object2ImplementingInterface } -unionMemberNames: { -FooBarUnion: "Foo" -| "Bar" -} unions: { Union: Union.FooBarUnion } @@ -40,190 +37,50 @@ Boolean: boolean // ------------------------------------------------------------ // export namespace Root { -export interface Query { -__typename: __typename<"Query"> -interface: { -type: Nullable<{ -kind: "named" -named: Interface.Interface -}> -namedType: Interface.Interface -args: null -} -id: { -type: Nullable<{ -kind: "named" -named: $.Scalars["ID"] -}> -namedType: $.Scalars["ID"] -args: null -} -idNonNull: { -type: { -kind: "named" -named: $.Scalars["ID"] -} -namedType: $.Scalars["ID"] -args: null -} -string: { -type: Nullable<{ -kind: "named" -named: $.Scalars["String"] -}> -namedType: $.Scalars["String"] -args: null -} -stringWithRequiredArg: { -type: Nullable<{ -kind: "named" -named: $.Scalars["String"] -}> -namedType: $.Scalars["String"] -args: { -type: { -string: $.Scalars["String"] -} -allOptional: false -} -} -stringWithArgs: { -type: Nullable<{ -kind: "named" -named: $.Scalars["String"] -}> -namedType: $.Scalars["String"] -args: { -type: { -string?: $.Scalars["String"] | null -int?: $.Scalars["Int"] | null -float?: $.Scalars["Float"] | null -boolean?: $.Scalars["Boolean"] | null -id?: $.Scalars["ID"] | null -} -allOptional: true -} -} -object: { -type: Nullable<{ -kind: "named" -named: Object.Object -}> -namedType: Object.Object -args: null -} -listListIntNonNull: { -type: { -kind: "list" -type: { -kind: "list" -type: { -kind: "named" -named: $.Scalars["Int"] -} -} -} -namedType: $.Scalars["Int"] -args: null -} -listListInt: { -type: { -kind: "nullable" -type: { -kind: "list" -type: { -kind: "nullable" -type: { -kind: "list" -type: Nullable<{ -kind: "named" -named: $.Scalars["Int"] -}> -} -} -} -} -namedType: $.Scalars["Int"] -args: null -} -listInt: { -type: { -kind: "nullable" -type: { -kind: "list" -type: Nullable<{ -kind: "named" -named: $.Scalars["Int"] -}> -} -} -namedType: $.Scalars["Int"] -args: null -} -listIntNonNull: { -type: { -kind: "list" -type: { -kind: "named" -named: $.Scalars["Int"] -} -} -namedType: $.Scalars["Int"] -args: null -} -objectNested: { -type: Nullable<{ -kind: "named" -named: Object.ObjectNested -}> -namedType: Object.ObjectNested -args: null -} -objectNonNull: { -type: { -kind: "named" -named: Object.Object -} -namedType: Object.Object -args: null -} -objectWithArgs: { -type: Nullable<{ -kind: "named" -named: Object.Object -}> -namedType: Object.Object -args: { -type: { -string?: $.Scalars["String"] | null -int?: $.Scalars["Int"] | null -float?: $.Scalars["Float"] | null -boolean?: $.Scalars["Boolean"] | null -id?: $.Scalars["ID"] | null -} -allOptional: true -} -} -fooBarUnion: { -type: Nullable<{ -kind: "named" -named: Union.FooBarUnion -}> -namedType: Union.FooBarUnion -args: null -} +export type Query = _.Object<"Query", { +interface: _.Field<_.Nullable> +id: _.Field<_.Nullable<_.Scalar.ID>> +idNonNull: _.Field<_.Scalar.ID> +string: _.Field<_.Nullable<_.Scalar.String>> +stringWithRequiredArg: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ +string: _.Scalar.String +}>> +stringWithArgs: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ +string: _.Nullable<_.Scalar.String> +int: _.Nullable<_.Scalar.Int> +float: _.Nullable<_.Scalar.Float> +boolean: _.Nullable<_.Scalar.Boolean> +id: _.Nullable<_.Scalar.ID> +}>> +stringWithArgEnum: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ +ABCEnum: _.Nullable +}>> +stringWithListArg: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ +ints: _.Nullable<_.List<_.Nullable<_.Scalar.Int>>> +}>> +stringWithListArgRequired: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ +ints: _.List<_.Nullable<_.Scalar.Int>> +}>> +object: _.Field<_.Nullable> +listListIntNonNull: _.Field<_.List<_.List<_.Scalar.Int>>> +listListInt: _.Field<_.Nullable<_.List<_.Nullable<_.List<_.Nullable<_.Scalar.Int>>>>>> +listInt: _.Field<_.Nullable<_.List<_.Nullable<_.Scalar.Int>>>> +listIntNonNull: _.Field<_.List<_.Scalar.Int>> +objectNested: _.Field<_.Nullable> +objectNonNull: _.Field +objectWithArgs: _.Field<_.Nullable, _.Args<{ +string: _.Nullable<_.Scalar.String> +int: _.Nullable<_.Scalar.Int> +float: _.Nullable<_.Scalar.Float> +boolean: _.Nullable<_.Scalar.Boolean> +id: _.Nullable<_.Scalar.ID> +}>> +fooBarUnion: _.Field<_.Nullable> /** * Query enum field documentation. */ -abcEnum: { -type: Nullable<{ -kind: "named" -named: Enum.ABCEnum +abcEnum: _.Field<_.Nullable> }> -namedType: Enum.ABCEnum -args: null -} -} } // ------------------------------------------------------------ // @@ -239,10 +96,7 @@ export namespace Enum { * "B" - Enum B member documentation. * "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) */ -export type ABCEnum = -| "A" -| "B" -| "C" +export type ABCEnum = _.Enum<"ABCEnum", ["A", "B", "C"] > } // ------------------------------------------------------------ // @@ -259,21 +113,9 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { -export interface Interface { -__interfacename: "Interface" -type: { -id: { -type: Nullable<{ -kind: "named" -named: $.Scalars["ID"] -}> -namedType: $.Scalars["ID"] -args: null -} -} -implementors: Object.Object1ImplementingInterface -| Object.Object2ImplementingInterface -} +export type Interface = _.Interface<"Interface", { +id: _.Field<_.Nullable<_.Scalar.ID>> +}, [Object.Object1ImplementingInterface, Object.Object2ImplementingInterface]> } // ------------------------------------------------------------ // @@ -284,138 +126,41 @@ export namespace Object { /** * Object documentation. */ -export interface Foo { -__typename: __typename<"Foo"> +export type Foo = _.Object<"Foo", { /** * Field documentation. * * @deprecated Field a is deprecated. */ -id: { -type: Nullable<{ -kind: "named" -named: $.Scalars["ID"] +id: _.Field<_.Nullable<_.Scalar.ID>> }> -namedType: $.Scalars["ID"] -args: null -} -} -export interface Bar { -__typename: __typename<"Bar"> -int: { -type: Nullable<{ -kind: "named" -named: $.Scalars["Int"] +export type Bar = _.Object<"Bar", { +int: _.Field<_.Nullable<_.Scalar.Int>> }> -namedType: $.Scalars["Int"] -args: null -} -} -export interface ObjectNested { -__typename: __typename<"ObjectNested"> -id: { -type: Nullable<{ -kind: "named" -named: $.Scalars["ID"] +export type ObjectNested = _.Object<"ObjectNested", { +id: _.Field<_.Nullable<_.Scalar.ID>> +object: _.Field<_.Nullable> }> -namedType: $.Scalars["ID"] -args: null -} -object: { -type: Nullable<{ -kind: "named" -named: Object.Object -}> -namedType: Object.Object -args: null -} -} -export interface Object { -__typename: __typename<"Object"> -string: { -type: Nullable<{ -kind: "named" -named: $.Scalars["String"] -}> -namedType: $.Scalars["String"] -args: null -} -int: { -type: Nullable<{ -kind: "named" -named: $.Scalars["Int"] -}> -namedType: $.Scalars["Int"] -args: null -} -float: { -type: Nullable<{ -kind: "named" -named: $.Scalars["Float"] +export type Object = _.Object<"Object", { +string: _.Field<_.Nullable<_.Scalar.String>> +int: _.Field<_.Nullable<_.Scalar.Int>> +float: _.Field<_.Nullable<_.Scalar.Float>> +boolean: _.Field<_.Nullable<_.Scalar.Boolean>> +id: _.Field<_.Nullable<_.Scalar.ID>> }> -namedType: $.Scalars["Float"] -args: null -} -boolean: { -type: Nullable<{ -kind: "named" -named: $.Scalars["Boolean"] -}> -namedType: $.Scalars["Boolean"] -args: null -} -id: { -type: Nullable<{ -kind: "named" -named: $.Scalars["ID"] -}> -namedType: $.Scalars["ID"] -args: null -} -} -export interface Object1ImplementingInterface { -__typename: __typename<"Object1ImplementingInterface"> -id: { -type: Nullable<{ -kind: "named" -named: $.Scalars["ID"] -}> -namedType: $.Scalars["ID"] -args: null -} -int: { -type: Nullable<{ -kind: "named" -named: $.Scalars["Int"] +export type Object1ImplementingInterface = _.Object<"Object1ImplementingInterface", { +id: _.Field<_.Nullable<_.Scalar.ID>> +int: _.Field<_.Nullable<_.Scalar.Int>> }> -namedType: $.Scalars["Int"] -args: null -} -} -export interface Object2ImplementingInterface { -__typename: __typename<"Object2ImplementingInterface"> -id: { -type: Nullable<{ -kind: "named" -named: $.Scalars["ID"] +export type Object2ImplementingInterface = _.Object<"Object2ImplementingInterface", { +id: _.Field<_.Nullable<_.Scalar.ID>> +boolean: _.Field<_.Nullable<_.Scalar.Boolean>> }> -namedType: $.Scalars["ID"] -args: null -} -boolean: { -type: Nullable<{ -kind: "named" -named: $.Scalars["Boolean"] -}> -namedType: $.Scalars["Boolean"] -args: null -} -} } // ------------------------------------------------------------ // @@ -426,10 +171,6 @@ export namespace Union { /** * Union documentation. */ -export interface FooBarUnion { -__unionname: "FooBarUnion" -type: Object.Foo -| Object.Bar -} +export type FooBarUnion = _.Union<"FooBarUnion",[Object.Foo, Object.Bar]> }" `; From 0e3cca5caef2cfab2a99dbdb6f264d914df0b959 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Thu, 14 Mar 2024 22:48:57 -0400 Subject: [PATCH 64/71] begin fixing result set --- src/ResultSet/ResultSet.test-d.ts | 2 +- src/ResultSet/ResultSet.ts | 80 ++++++++++++++++--------------- src/Schema/__.ts | 2 +- 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 239a7d29e..2f5c9cca0 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -6,7 +6,7 @@ import type { SelectionSet } from '../SelectionSet/__.js' import type { ResultSet } from './__.js' type I = Schema.$.Index -type RS<$S extends SelectionSet.Query> = ResultSet.Query<$S, I> +type RS<$selectionSet extends SelectionSet.Query> = ResultSet.Query<$selectionSet, I> // dprint-ignore test(`general`, () => { diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index 2705a778a..c6f78d1fd 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -18,45 +18,45 @@ export type Subscription<$SelectionSetSubscription extends object, $Index extend SimplifyDeep, $Index>> // dprint-ignore -type Node<$SelectionSet, $Node extends Schema.Node, $Index extends Schema.Index> = - $Node extends Schema.Union ? Union<$SelectionSet, $Node, $Index> : - $Node extends Schema.Interface$ ? Interface$<$SelectionSet, $Node, $Index> : +type Node<$SelectionSet, $Node extends Schema.Named.Any, $Index extends Schema.Index> = + $Node extends Schema.Named.Union ? Union<$SelectionSet, $Node, $Index> : + $Node extends Schema.Named.Interface ? Interface$<$SelectionSet, $Node, $Index> : // todo handle types where each union member implements the same interface -- this should yield support for both interface and union features - $Node extends Schema.Object ? Object<$SelectionSet, $Node, $Index> : - $Node extends Schema.Scalar ? $Node - : Errors.UnknownNode<$Node> + $Node extends Schema.Named.Object ? Object<$SelectionSet, $Node, $Index> : + $Node extends Schema.Named.Scalar.Any ? $Node : + Errors.UnknownNode<$Node> // dprint-ignore -export type Object<$SelectionSet, $Object extends Schema.Object, $Index extends Schema.Index> = +export type Object<$SelectionSet, $Object extends Schema.Named.Object, $Index extends Schema.Index> = SelectionSet.IsSelectScalarsWildcard<$SelectionSet> extends true + /** * Handle Scalars Wildcard */ - ? - { - [$Key in keyof $Object as $Object[$Key] extends Schema.FieldScalar ? $Key : never]: - Field<$SelectionSet, Schema.AsField<$Object[$Key]>, $Index> - } - : ( - /** - * Handle fields in regular way. - */ - | SelectionSet.ResolveAliasTargets<{ - [K in keyof SelectionSet.OmitNegativeIndicators<$SelectionSet> & string as K extends `${K}_as_${infer s}` ? s : K]: - SimplifyDeep extends keyof $Object - ? Field<$SelectionSet[K], Schema.AsField<$Object[SelectionSet.AliasNameOrigin]>, $Index> - : Errors.UnknownFieldName> - }> - ) + ? + { + [$Key in keyof $Object as $Object[$Key] extends Schema.FieldScalar ? $Key : never]: + Field<$SelectionSet, Schema.Field.As<$Object[$Key]>, $Index> + } + /** + * Handle fields in regular way. + */ + : + SelectionSet.ResolveAliasTargets<{ + [K in keyof SelectionSet.OmitNegativeIndicators<$SelectionSet> & string as K extends `${K}_as_${infer s}` ? s : K]: + SelectionSet.AliasNameOrigin extends keyof $Object['fields'] + ? Field<$SelectionSet[K], $Object['fields'][SelectionSet.AliasNameOrigin], $Index> + : Errors.UnknownFieldName + }> // dprint-ignore -type Union<$SelectionSet, $Node extends Schema.Union, $Index extends Schema.Index> = +type Union<$SelectionSet, $Node extends Schema.Named.Union, $Index extends Schema.Index> = Values<{ [$ObjectName in $Node['type']['__typename']['namedType']]: Object & SelectionSet.UnionOmitFragments<$SelectionSet>, $Index['objects'][$ObjectName], $Index> }> -type Interface$<$SelectionSet, $Node extends Schema.Interface$, $Index extends Schema.Index> = Values< +type Interface$<$SelectionSet, $Node extends Schema.Named.Interface, $Index extends Schema.Index> = Values< { [$ObjectName in $Node['implementors']['__typename']['namedType']]: Object< GetKeyOr<$SelectionSet, `on${$ObjectName}`, {}> & SelectionSet.UnionOmitFragments<$SelectionSet>, @@ -67,25 +67,27 @@ type Interface$<$SelectionSet, $Node extends Schema.Interface$, $Index extends S > // dprint-ignore -type Field<$SelectionSet, $Field extends Schema.Field, $Index extends Schema.Index> = - $SelectionSet extends SelectionSet.Directive.Include.Negative | SelectionSet.Directive.Skip.Positive ? null : - ( - | FieldDirectiveInclude<$SelectionSet> - | FieldDirectiveSkip<$SelectionSet> - | FieldType<$SelectionSet, $Field['type'], $Index> - ) +type Field<$SelectionSet, $Field extends Schema.Field.Field, $Index extends Schema.Index> = + $SelectionSet extends SelectionSet.Directive.Include.Negative | SelectionSet.Directive.Skip.Positive ? + null : + ( + | FieldDirectiveInclude<$SelectionSet> + | FieldDirectiveSkip<$SelectionSet> + | FieldType<$SelectionSet, $Field['type'], $Index> + ) // dprint-ignore type FieldType< -$SelectionSet, - $FieldType extends Schema.FieldTypeNamed | Schema.FieldTypeList | Schema.FieldTypeNullable | Schema.FieldTypeLiteral, + $SelectionSet, + $Type extends Schema.Field.Type.Any, $Index extends Schema.Index > = - $FieldType extends Schema.FieldTypeLiteral ? $FieldType['value'] : - $FieldType extends Schema.FieldTypeNullable ? null | FieldType<$SelectionSet, $FieldType['type'], $Index> : - $FieldType extends Schema.FieldTypeList ? FieldType<$SelectionSet, $FieldType['type'], $Index>[] : - $FieldType extends Schema.FieldTypeNamed ? Node<$SelectionSet, $FieldType['named'], $Index> - : never + $Type extends Schema.Field.Type.Nullable ? null | FieldType<$SelectionSet, $InnerType, $Index> : + $Type extends Schema.Named.Scalar.Any ? ReturnType<$Type['constructor']> : + // $Type extends Schema.FieldTypeNullable ? null | FieldType<$SelectionSet, $Type['type'], $Index> : + // $Type extends Schema.FieldTypeList ? FieldType<$SelectionSet, $Type['type'], $Index>[] : + // $Type extends Schema.FieldTypeNamed ? Node<$SelectionSet, $Type['named'], $Index> : + $Type // dprint-ignore type FieldDirectiveInclude<$SelectionSet> = diff --git a/src/Schema/__.ts b/src/Schema/__.ts index 5fea0ca18..3574a0b54 100644 --- a/src/Schema/__.ts +++ b/src/Schema/__.ts @@ -1,4 +1,4 @@ -export * as Schema from './__namespaced.js' +export * as Schema from './__Schema.js' export { Args, As, Field } from './Field/__.js' export { __typename, List, Nullable } from './Field/Type.js' export { Index } from './Index.js' From 0f406218056e57c65d9b465c7e7e0b7e3bef00e6 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 15 Mar 2024 22:22:19 -0400 Subject: [PATCH 65/71] more cases --- src/ResultSet/ResultSet.test-d.ts | 19 +++++++++++++------ src/ResultSet/ResultSet.ts | 18 ++++++++++-------- src/Schema/Field/Field.ts | 2 +- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 2f5c9cca0..bf2d6a23e 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -10,6 +10,9 @@ type RS<$selectionSet extends SelectionSet.Query> = ResultSet.Query<$selectio // dprint-ignore test(`general`, () => { + // __typename + expectTypeOf>().toEqualTypeOf<{ __typename: 'Query' }>() + // Scalar expectTypeOf>().toEqualTypeOf<{ id: null | string }>() expectTypeOf>().toEqualTypeOf<{ id: null | string }>() @@ -20,10 +23,20 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ id: null | string }>() expectTypeOf>().toEqualTypeOf<{ id: null | string }>() + // List + expectTypeOf>().toEqualTypeOf<{ listIntNonNull: number[] }>() + expectTypeOf>().toEqualTypeOf<{ listInt: null|(null|number)[] }>() + expectTypeOf>().toEqualTypeOf<{ listListIntNonNull: number[][] }>() + expectTypeOf>().toEqualTypeOf<{ listListInt: null|((null|(null|number)[])[]) }>() + + // Enum + expectTypeOf>().toEqualTypeOf<{ abcEnum: null|'A'|'B'|'C' }>() + // Object expectTypeOf>().toEqualTypeOf<{ object: null | { id: string | null } }>() // non-nullable expectTypeOf>().toEqualTypeOf<{ objectNonNull: { id: string | null } }>() + // scalars-wildcard expectTypeOf>().toEqualTypeOf<{ objectNonNull: { __typename: "Object"; string: null|string; int: null|number; float: null|number; boolean: null|boolean; id: null|string; } }>() // scalars-wildcard with nested object @@ -47,12 +60,6 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ interface: null | { __typename: 'Object1ImplementingInterface' } | {}}>() expectTypeOf>().toEqualTypeOf<{ interface: null | { __typename: 'Object1ImplementingInterface', id: null | string, int: null|number} | { __typename: 'Object2ImplementingInterface', id: null | string; boolean:null|boolean} }>() - // List - expectTypeOf>().toEqualTypeOf<{ listIntNonNull: number[] }>() - expectTypeOf>().toEqualTypeOf<{ listInt: null|(null|number)[] }>() - expectTypeOf>().toEqualTypeOf<{ listListIntNonNull: number[][] }>() - expectTypeOf>().toEqualTypeOf<{ listListInt: null|((null|(null|number)[])[]) }>() - // Alias // scalar expectTypeOf>().toEqualTypeOf<{ id2: null | string }>() diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index c6f78d1fd..c4d9bb3a7 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -35,8 +35,9 @@ export type Object<$SelectionSet, $Object extends Schema.Named.Object, $Index ex */ ? { - [$Key in keyof $Object as $Object[$Key] extends Schema.FieldScalar ? $Key : never]: - Field<$SelectionSet, Schema.Field.As<$Object[$Key]>, $Index> + [$Key in keyof $Object['fields'] as $Object['fields'][$Key] extends Schema.Field.Field | {'typeUnwrapped':{kind:'Scalar'}} ? $Key : never]: + // $Object['fields'][$Key] + Field<$SelectionSet, Schema.Field.As<$Object['fields'][$Key]>, $Index> } /** * Handle fields in regular way. @@ -82,12 +83,13 @@ type FieldType< $Type extends Schema.Field.Type.Any, $Index extends Schema.Index > = - $Type extends Schema.Field.Type.Nullable ? null | FieldType<$SelectionSet, $InnerType, $Index> : - $Type extends Schema.Named.Scalar.Any ? ReturnType<$Type['constructor']> : - // $Type extends Schema.FieldTypeNullable ? null | FieldType<$SelectionSet, $Type['type'], $Index> : - // $Type extends Schema.FieldTypeList ? FieldType<$SelectionSet, $Type['type'], $Index>[] : - // $Type extends Schema.FieldTypeNamed ? Node<$SelectionSet, $Type['named'], $Index> : - $Type + $Type extends Schema.Field.Type.__typename ? $Value : + $Type extends Schema.Field.Type.Nullable ? null | FieldType<$SelectionSet, $InnerType, $Index> : + $Type extends Schema.Field.Type.List ? Array> : + $Type extends Schema.Named.Enum ? $Members[number] : + $Type extends Schema.Named.Scalar.Any ? ReturnType<$Type['constructor']> : + $Type extends Schema.Named.Object ? Object<$SelectionSet,$Type,$Index> : + $Type // dprint-ignore type FieldDirectiveInclude<$SelectionSet> = diff --git a/src/Schema/Field/Field.ts b/src/Schema/Field/Field.ts index 37ebe7d1d..c4923f413 100644 --- a/src/Schema/Field/Field.ts +++ b/src/Schema/Field/Field.ts @@ -9,7 +9,7 @@ export type As = T extends Field ? T : never export type Enum<$Args extends Args | null = null> = Field -export type Scalar<$Args extends Args | null = null> = Field +export type Scalar<$Args extends Args | null = Args | null> = Field export type String<$Args extends Args | null = null> = Field From f53df0672e558b79ef194abde4c8bbff4a2c9293 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 16 Mar 2024 23:11:21 -0400 Subject: [PATCH 66/71] tests passing! --- src/ResultSet/ResultSet.test-d.ts | 4 + src/ResultSet/ResultSet.ts | 84 +++++++------------ src/Schema/Field/Type.ts | 3 +- src/SelectionSet/SelectionSet.test-d.ts | 3 + src/SelectionSet/SelectionSet.ts | 41 ++++----- src/client.ts | 2 +- tests/builder/_/schema.graphql | 9 ++ tests/builder/_/schema.ts | 17 +++- .../__snapshots__/generate.test.ts.snap | 14 ++++ 9 files changed, 101 insertions(+), 76 deletions(-) diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index bf2d6a23e..4555a787c 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -50,6 +50,10 @@ test(`general`, () => { expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | {} | { id: null|string } }>() expectTypeOf>().toEqualTypeOf<{ fooBarUnion: null | { __typename: "Bar" } | { __typename: "Foo"; id: null|string } }>() + // Union fragments Case + expectTypeOf>().toEqualTypeOf<{ lowerCaseUnion: null | { __typename: 'lowerCaseObject'; id: null|string } | { __typename: 'lowerCaseObject2'; int: null|number } }>() + + // Interface expectTypeOf>().toEqualTypeOf<{ interface: null | { id: null | string} | {} }>() expectTypeOf>().toEqualTypeOf<{ interface: null | { int: null | number} | {} }>() diff --git a/src/ResultSet/ResultSet.ts b/src/ResultSet/ResultSet.ts index c4d9bb3a7..0c4245009 100644 --- a/src/ResultSet/ResultSet.ts +++ b/src/ResultSet/ResultSet.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/ban-types */ -import type { GetKeyOr, SimplifyDeep, Values } from '../lib/prelude.js' +import type { Simplify } from 'type-fest' +import type { GetKeyOr, SimplifyDeep } from '../lib/prelude.js' import type { TSError } from '../lib/TSError.js' import type { Schema } from '../Schema/__.js' import type { SelectionSet } from '../SelectionSet/__.js' @@ -18,16 +19,7 @@ export type Subscription<$SelectionSetSubscription extends object, $Index extend SimplifyDeep, $Index>> // dprint-ignore -type Node<$SelectionSet, $Node extends Schema.Named.Any, $Index extends Schema.Index> = - $Node extends Schema.Named.Union ? Union<$SelectionSet, $Node, $Index> : - $Node extends Schema.Named.Interface ? Interface$<$SelectionSet, $Node, $Index> : - // todo handle types where each union member implements the same interface -- this should yield support for both interface and union features - $Node extends Schema.Named.Object ? Object<$SelectionSet, $Node, $Index> : - $Node extends Schema.Named.Scalar.Any ? $Node : - Errors.UnknownNode<$Node> - -// dprint-ignore -export type Object<$SelectionSet, $Object extends Schema.Named.Object, $Index extends Schema.Index> = +export type Object<$SelectionSet, $Node extends Schema.Named.Object, $Index extends Schema.Index> = SelectionSet.IsSelectScalarsWildcard<$SelectionSet> extends true /** @@ -35,9 +27,10 @@ export type Object<$SelectionSet, $Object extends Schema.Named.Object, $Index ex */ ? { - [$Key in keyof $Object['fields'] as $Object['fields'][$Key] extends Schema.Field.Field | {'typeUnwrapped':{kind:'Scalar'}} ? $Key : never]: - // $Object['fields'][$Key] - Field<$SelectionSet, Schema.Field.As<$Object['fields'][$Key]>, $Index> + [$Key in keyof $Node['fields'] as $Node['fields'][$Key] extends Schema.Field.Field | {'typeUnwrapped':{kind:'Scalar'}} ? $Key : never]: + // eslint-disable-next-line + // @ts-ignore infinite depth issue, can this be fixed? + Field<$SelectionSet, Schema.Field.As<$Node['fields'][$Key]>, $Index> } /** * Handle fields in regular way. @@ -45,27 +38,28 @@ export type Object<$SelectionSet, $Object extends Schema.Named.Object, $Index ex : SelectionSet.ResolveAliasTargets<{ [K in keyof SelectionSet.OmitNegativeIndicators<$SelectionSet> & string as K extends `${K}_as_${infer s}` ? s : K]: - SelectionSet.AliasNameOrigin extends keyof $Object['fields'] - ? Field<$SelectionSet[K], $Object['fields'][SelectionSet.AliasNameOrigin], $Index> - : Errors.UnknownFieldName + SelectionSet.AliasNameOrigin extends keyof $Node['fields'] + ? Field<$SelectionSet[K], $Node['fields'][SelectionSet.AliasNameOrigin], $Index> + : Errors.UnknownFieldName, $Node> }> // dprint-ignore type Union<$SelectionSet, $Node extends Schema.Named.Union, $Index extends Schema.Index> = - Values<{ - [$ObjectName in $Node['type']['__typename']['namedType']]: - Object & SelectionSet.UnionOmitFragments<$SelectionSet>, $Index['objects'][$ObjectName], $Index> - }> + OnTypeFragment<$SelectionSet,$Node['members'][number], $Index> + +// dprint-ignore +type Interface<$SelectionSet, $Node extends Schema.Named.Interface, $Index extends Schema.Index> = + OnTypeFragment<$SelectionSet, $Node['implementors'][number], $Index> -type Interface$<$SelectionSet, $Node extends Schema.Named.Interface, $Index extends Schema.Index> = Values< - { - [$ObjectName in $Node['implementors']['__typename']['namedType']]: Object< - GetKeyOr<$SelectionSet, `on${$ObjectName}`, {}> & SelectionSet.UnionOmitFragments<$SelectionSet>, - $Index['objects'][$ObjectName], - $Index - > - } -> +// dprint-ignore +type OnTypeFragment<$SelectionSet, $Node extends Schema.Named.Object, $Index extends Schema.Index> = + $Node extends any // force distribution + ? Object< + GetKeyOr<$SelectionSet, `on${Capitalize<$Node['fields']['__typename']['type']['type']>}`, {}> & SelectionSet.OmitOnTypeFragments<$SelectionSet>, + $Node, + $Index + > + : never // dprint-ignore type Field<$SelectionSet, $Field extends Schema.Field.Field, $Index extends Schema.Index> = @@ -82,14 +76,17 @@ type FieldType< $SelectionSet, $Type extends Schema.Field.Type.Any, $Index extends Schema.Index -> = +> =Simplify< $Type extends Schema.Field.Type.__typename ? $Value : $Type extends Schema.Field.Type.Nullable ? null | FieldType<$SelectionSet, $InnerType, $Index> : $Type extends Schema.Field.Type.List ? Array> : $Type extends Schema.Named.Enum ? $Members[number] : $Type extends Schema.Named.Scalar.Any ? ReturnType<$Type['constructor']> : $Type extends Schema.Named.Object ? Object<$SelectionSet,$Type,$Index> : - $Type + $Type extends Schema.Named.Interface ? Interface<$SelectionSet,$Type,$Index> : + $Type extends Schema.Named.Union ? Union<$SelectionSet,$Type,$Index> : + TSError<'FieldType', `Unknown type`, { $Type: $Type }> + > // dprint-ignore type FieldDirectiveInclude<$SelectionSet> = @@ -107,26 +104,5 @@ type FieldDirectiveSkip<$SelectionSet> = // dprint-ignore export namespace Errors { - export type UnknownNode<$Node extends Schema.Node> = - TSError<'Node', `Unknown case`, { $Node: $Node }> - - export type UnknownFieldName<$FieldName extends string, $Node extends Schema.Object> = - TSError<'Object', `field "${$FieldName}" does not exist on schema object "${$Node['__typename']['namedType']}"`> + export type UnknownFieldName<$FieldName extends string, $Object extends Schema.Named.Object> = TSError<'Object', `field "${$FieldName}" does not exist on object "${$Object['fields']['__typename']['type']['type']}"`> } - -// type SelectField<$Objekt extends Schema.Object, $SelectionObjekt extends SelectionObjekt, $FieldName extends keyof $SelectionObjekt & string> = -// $FieldName extends keyof $Objekt ? $Objekt[$FieldName] extends Node ? $SelectionObjekt[$FieldName] extends SelectionScalar ? SelectScalar<$Objekt[$FieldName], $SelectionObjekt[$FieldName]> -// : $SelectionObjekt[$FieldName] extends SelectionObjekt ? SelectObjekt<$Objekt[$FieldName], $SelectionObjekt[$FieldName]> -// : TSError<'SelectionObjektField', `selection object field "${$FieldName}" is of unknown type.`> -// : [$SelectionObjekt,$FieldName,$Objekt[$FieldName],TSError<'SelectObjektField', `object selection field "${$FieldName}" on schema object "${$Objekt['__typename']}" is not a Node.`>] -// // : $Field extends `on_${infer $TypeName}` ? TSError<'SelectObjektField', `fragment field "on_${$TypeName}" should be handled by caller.`> -// : TSError<'SelectObjektField', `object selection field "${$FieldName}" does not exist on schema object "${$Objekt['__typename']}"`> - -// $Node extends Nullable ? null | SelectObjekt, $Selection> -// : $Node extends Scalar ? TSError<'SelectObjekt','$Node is Scalar (should be Object).'> -// : $Node extends List ? TSError<'SelectObjekt','$Node is List (should be Object).'> -// : $Node extends List ? SelectObjekt_<$Node[number], $Selection>[] -// // : $Node extends Interface ? SelectInterface<$Node, $Selection> -// : $Node extends Objekt ? SelectObjekt_<$Node, $Selection> -// : TSError<'SelectObjekt','$Node is unknown type (should be Objekt).', { $Node: $Node }> -// : TSError<'SelectObjekt','$Selection is not an object.', { $Selection: $Selection }> diff --git a/src/Schema/Field/Type.ts b/src/Schema/Field/Type.ts index 8f64a5af6..63e26b4ed 100644 --- a/src/Schema/Field/Type.ts +++ b/src/Schema/Field/Type.ts @@ -23,8 +23,9 @@ export const nullable = <$Type extends __typename | List>(type: $Type) }) export const list = <$Type extends Any>(type: $Type): List<$Type> => ({ kind: `list`, type }) +// todo extends any because of infinite depth issue in generated schema types // dprint-ignore -export type Unwrap<$Type extends Any> = +export type Unwrap<$Type extends any> = $Type extends List ? Unwrap<$innerType> : $Type extends Nullable ? Unwrap<$innerType> : $Type extends __typename ? $Type['type'] : diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index 112bafb52..356055734 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -54,6 +54,9 @@ test(`Query`, () => { // @ts-expect-error no a assertType({ fooBarUnion: { onBar: { int2: true } } }) + // Union fragments Case + assertType({ lowerCaseUnion: { onLowerCaseObject: { id: true }, onLowerCaseObject2: { int: true } } }) + // Interface assertType({ interface: { id: true } }) assertType({ interface: { id: { $defer: true } } }) diff --git a/src/SelectionSet/SelectionSet.ts b/src/SelectionSet/SelectionSet.ts index 91d60f369..c3013bc3c 100644 --- a/src/SelectionSet/SelectionSet.ts +++ b/src/SelectionSet/SelectionSet.ts @@ -84,10 +84,8 @@ $Field['args'] extends Schema.Field.Args ? $Field['args']['allOptional'] extend {} // dprint-ignore -type Interface< - $Node extends Schema.Named.Interface, - $Index extends Schema.Index, -> = +type Interface<$Node extends Schema.Named.Interface, $Index extends Schema.Index> = + & InterfaceDistributed<$Node['implementors'][number], $Index> & Fields< & $Node['fields'] & { @@ -95,24 +93,29 @@ type Interface< }, $Index > - & { - [Key in $Node['implementors'][number]['fields']['__typename']['typeUnwrapped'] as `on${Capitalize}`]?: - Object, $Index> & FieldDirectives - } -// TODO why does $object not get passed to this in a distributed way? // dprint-ignore -type Union< - $Union extends Schema.Named.Union, - $Index extends Schema.Index, -> = - & { - [Key in $Union['members'][number]['fields']['__typename']['typeUnwrapped'] as `on${Capitalize}`]?: - Object, $Index> & FieldDirectives +type InterfaceDistributed<$Node extends Schema.Named.Object, $Index extends Schema.Index> = + $Node extends any + ? { + [$typename in $Node['fields']['__typename']['type']['type'] as `on${Capitalize<$typename>}`]?: + Object<$Node, $Index> & FieldDirectives } - & { - __typename?: NoArgsIndicator + : never + +// dprint-ignore +type Union<$Node extends Schema.Named.Union, $Index extends Schema.Index> = + & UnionDistributed<$Node['members'][number], $Index> + & { __typename?: NoArgsIndicator } + +// dprint-ignore +type UnionDistributed<$Object extends Schema.Named.Object,$Index extends Schema.Index> = + $Object extends any + ? { + [$typename in $Object['fields']['__typename']['type']['type'] as `on${Capitalize<$typename>}`]?: + Object<$Object, $Index> & FieldDirectives } + : never /** * Helpers @@ -129,7 +132,7 @@ export type UnionExtractFragmentNames = Values< [Key in keyof T]: UnionFragmentExtractName } > -export type UnionOmitFragments = { +export type OmitOnTypeFragments = { [$K in keyof T as $K extends `on${StringNonEmpty}` ? never : $K]: T[$K] } diff --git a/src/client.ts b/src/client.ts index 6f09d4a45..af368abd0 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,5 +1,5 @@ import type { ResultSet } from './ResultSet/__.js' -import type { Index } from './Schema/Schema.js' +import type { Index } from './Schema/__.js' import type { SelectionSet } from './SelectionSet/__.js' // dprint-ignore diff --git a/tests/builder/_/schema.graphql b/tests/builder/_/schema.graphql index 289a7e895..f4edb7118 100644 --- a/tests/builder/_/schema.graphql +++ b/tests/builder/_/schema.graphql @@ -21,6 +21,7 @@ type Query { Query enum field documentation. """ abcEnum: ABCEnum + lowerCaseUnion: lowerCaseUnion } """ @@ -47,6 +48,14 @@ type ObjectNested { object: Object } +type lowerCaseObject { + id: ID +} +type lowerCaseObject2 { + int: Int +} +union lowerCaseUnion = lowerCaseObject | lowerCaseObject2 + type Object { string: String int: Int diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 92244d66b..3c795cd83 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -11,12 +11,16 @@ export namespace $ { Foo: Object.Foo Bar: Object.Bar ObjectNested: Object.ObjectNested + lowerCaseObject: Object.lowerCaseObject + lowerCaseObject2: Object.lowerCaseObject2 Object: Object.Object Object1ImplementingInterface: Object.Object1ImplementingInterface Object2ImplementingInterface: Object.Object2ImplementingInterface } unions: { - Union: Union.FooBarUnion + Union: + | Union.FooBarUnion + | Union.lowerCaseUnion } scalars: Scalars } @@ -95,6 +99,7 @@ export namespace Root { * Query enum field documentation. */ abcEnum: _.Field<_.Nullable> + lowerCaseUnion: _.Field<_.Nullable> }> } @@ -158,6 +163,14 @@ export namespace Object { object: _.Field<_.Nullable> }> + export type lowerCaseObject = _.Object<'lowerCaseObject', { + id: _.Field<_.Nullable<_.Scalar.ID>> + }> + + export type lowerCaseObject2 = _.Object<'lowerCaseObject2', { + int: _.Field<_.Nullable<_.Scalar.Int>> + }> + export type Object = _.Object<'Object', { string: _.Field<_.Nullable<_.Scalar.String>> int: _.Field<_.Nullable<_.Scalar.Int>> @@ -186,4 +199,6 @@ export namespace Union { * Union documentation. */ export type FooBarUnion = _.Union<'FooBarUnion', [Object.Foo, Object.Bar]> + + export type lowerCaseUnion = _.Union<'lowerCaseUnion', [Object.lowerCaseObject, Object.lowerCaseObject2]> } diff --git a/tests/builder/__snapshots__/generate.test.ts.snap b/tests/builder/__snapshots__/generate.test.ts.snap index d1d1df790..c9f54f5f4 100644 --- a/tests/builder/__snapshots__/generate.test.ts.snap +++ b/tests/builder/__snapshots__/generate.test.ts.snap @@ -14,12 +14,15 @@ objects: { Foo: Object.Foo Bar: Object.Bar ObjectNested: Object.ObjectNested +lowerCaseObject: Object.lowerCaseObject +lowerCaseObject2: Object.lowerCaseObject2 Object: Object.Object Object1ImplementingInterface: Object.Object1ImplementingInterface Object2ImplementingInterface: Object.Object2ImplementingInterface } unions: { Union: Union.FooBarUnion +| Union.lowerCaseUnion } scalars: Scalars } @@ -80,6 +83,7 @@ fooBarUnion: _.Field<_.Nullable> * Query enum field documentation. */ abcEnum: _.Field<_.Nullable> +lowerCaseUnion: _.Field<_.Nullable> }> } @@ -144,6 +148,14 @@ id: _.Field<_.Nullable<_.Scalar.ID>> object: _.Field<_.Nullable> }> +export type lowerCaseObject = _.Object<"lowerCaseObject", { +id: _.Field<_.Nullable<_.Scalar.ID>> +}> + +export type lowerCaseObject2 = _.Object<"lowerCaseObject2", { +int: _.Field<_.Nullable<_.Scalar.Int>> +}> + export type Object = _.Object<"Object", { string: _.Field<_.Nullable<_.Scalar.String>> int: _.Field<_.Nullable<_.Scalar.Int>> @@ -172,5 +184,7 @@ export namespace Union { * Union documentation. */ export type FooBarUnion = _.Union<"FooBarUnion",[Object.Foo, Object.Bar]> + +export type lowerCaseUnion = _.Union<"lowerCaseUnion",[Object.lowerCaseObject, Object.lowerCaseObject2]> }" `; From a2a14bdb98d84ea343d1220a32b42d724be34b7b Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 16 Mar 2024 23:12:43 -0400 Subject: [PATCH 67/71] deps again --- package.json | 16 +- pnpm-lock.yaml | 446 +++++++++++++++++++++++-------------------------- 2 files changed, 216 insertions(+), 246 deletions(-) diff --git a/package.json b/package.json index 8a554578d..92f06e35b 100644 --- a/package.json +++ b/package.json @@ -68,9 +68,9 @@ "@types/body-parser": "^1.19.5", "@types/express": "^4.17.21", "@types/json-bigint": "^1.0.4", - "@types/node": "^20.11.20", - "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.0.2", + "@types/node": "^20.11.28", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", "apollo-server-express": "^3.13.0", "body-parser": "^1.20.2", "doctoc": "^2.2.1", @@ -82,15 +82,15 @@ "eslint-plugin-prefer-arrow": "^1.2.3", "eslint-plugin-simple-import-sort": "^12.0.0", "eslint-plugin-tsdoc": "^0.2.17", - "express": "^4.18.2", + "express": "^4.18.3", "get-port": "^7.0.0", "graphql": "^16.8.1", "graphql-tag": "^2.12.6", - "happy-dom": "^13.5.0", + "happy-dom": "^13.8.6", "json-bigint": "^1.0.0", "tsx": "^4.7.1", - "type-fest": "^4.10.3", - "typescript": "^5.3.3", - "vitest": "^1.3.1" + "type-fest": "^4.12.0", + "typescript": "^5.4.2", + "vitest": "^1.4.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4d0c08992..b7c10b95e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,17 +29,17 @@ devDependencies: specifier: ^1.0.4 version: 1.0.4 '@types/node': - specifier: ^20.11.20 - version: 20.11.20 + specifier: ^20.11.28 + version: 20.11.28 '@typescript-eslint/eslint-plugin': - specifier: ^7.0.2 - version: 7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.57.0)(typescript@5.3.3) + specifier: ^7.2.0 + version: 7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@5.4.2) '@typescript-eslint/parser': - specifier: ^7.0.2 - version: 7.0.2(eslint@8.57.0)(typescript@5.3.3) + specifier: ^7.2.0 + version: 7.2.0(eslint@8.57.0)(typescript@5.4.2) apollo-server-express: specifier: ^3.13.0 - version: 3.13.0(express@4.18.2)(graphql@16.8.1) + version: 3.13.0(express@4.18.3)(graphql@16.8.1) body-parser: specifier: ^1.20.2 version: 1.20.2 @@ -54,10 +54,10 @@ devDependencies: version: 8.57.0 eslint-config-prisma: specifier: ^0.2.0 - version: 0.2.0(@typescript-eslint/eslint-plugin@7.0.2)(@typescript-eslint/parser@7.0.2)(eslint-plugin-deprecation@2.0.0)(eslint-plugin-only-warn@1.1.0)(eslint-plugin-prefer-arrow@1.2.3)(eslint-plugin-simple-import-sort@12.0.0)(eslint-plugin-tsdoc@0.2.17)(eslint@8.57.0) + version: 0.2.0(@typescript-eslint/eslint-plugin@7.2.0)(@typescript-eslint/parser@7.2.0)(eslint-plugin-deprecation@2.0.0)(eslint-plugin-only-warn@1.1.0)(eslint-plugin-prefer-arrow@1.2.3)(eslint-plugin-simple-import-sort@12.0.0)(eslint-plugin-tsdoc@0.2.17)(eslint@8.57.0) eslint-plugin-deprecation: specifier: ^2.0.0 - version: 2.0.0(eslint@8.57.0)(typescript@5.3.3) + version: 2.0.0(eslint@8.57.0)(typescript@5.4.2) eslint-plugin-only-warn: specifier: ^1.1.0 version: 1.1.0 @@ -71,8 +71,8 @@ devDependencies: specifier: ^0.2.17 version: 0.2.17 express: - specifier: ^4.18.2 - version: 4.18.2 + specifier: ^4.18.3 + version: 4.18.3 get-port: specifier: ^7.0.0 version: 7.0.0 @@ -83,8 +83,8 @@ devDependencies: specifier: ^2.12.6 version: 2.12.6(graphql@16.8.1) happy-dom: - specifier: ^13.5.0 - version: 13.5.0 + specifier: ^13.8.6 + version: 13.8.6 json-bigint: specifier: ^1.0.0 version: 1.0.0 @@ -92,14 +92,14 @@ devDependencies: specifier: ^4.7.1 version: 4.7.1 type-fest: - specifier: ^4.10.3 - version: 4.10.3 + specifier: ^4.12.0 + version: 4.12.0 typescript: - specifier: ^5.3.3 - version: 5.3.3 + specifier: ^5.4.2 + version: 5.4.2 vitest: - specifier: ^1.3.1 - version: 1.3.1(@types/node@20.11.20)(happy-dom@13.5.0) + specifier: ^1.4.0 + version: 1.4.0(@types/node@20.11.28)(happy-dom@13.8.6) packages: @@ -237,7 +237,7 @@ packages: /@apollographql/graphql-playground-html@1.6.29: resolution: {integrity: sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==} dependencies: - xss: 1.0.14 + xss: 1.0.15 dev: true /@dprint/darwin-arm64@0.45.0: @@ -1012,104 +1012,104 @@ packages: resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} dev: true - /@rollup/rollup-android-arm-eabi@4.12.0: - resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==} + /@rollup/rollup-android-arm-eabi@4.13.0: + resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.12.0: - resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==} + /@rollup/rollup-android-arm64@4.13.0: + resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.12.0: - resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==} + /@rollup/rollup-darwin-arm64@4.13.0: + resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-x64@4.12.0: - resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==} + /@rollup/rollup-darwin-x64@4.13.0: + resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.12.0: - resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==} + /@rollup/rollup-linux-arm-gnueabihf@4.13.0: + resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.12.0: - resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==} + /@rollup/rollup-linux-arm64-gnu@4.13.0: + resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.12.0: - resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==} + /@rollup/rollup-linux-arm64-musl@4.13.0: + resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.12.0: - resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==} + /@rollup/rollup-linux-riscv64-gnu@4.13.0: + resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.12.0: - resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==} + /@rollup/rollup-linux-x64-gnu@4.13.0: + resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.12.0: - resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==} + /@rollup/rollup-linux-x64-musl@4.13.0: + resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.12.0: - resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==} + /@rollup/rollup-win32-arm64-msvc@4.13.0: + resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.12.0: - resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==} + /@rollup/rollup-win32-ia32-msvc@4.13.0: + resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.12.0: - resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==} + /@rollup/rollup-win32-x64-msvc@4.13.0: + resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==} cpu: [x64] os: [win32] requiresBuild: true @@ -1147,27 +1147,27 @@ packages: /@types/accepts@1.3.7: resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.11.28 dev: true /@types/body-parser@1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.38 - '@types/node': 20.11.20 + '@types/node': 20.11.28 dev: true /@types/body-parser@1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.38 - '@types/node': 20.11.20 + '@types/node': 20.11.28 dev: true /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.11.28 dev: true /@types/cors@2.8.12: @@ -1187,16 +1187,16 @@ packages: /@types/express-serve-static-core@4.17.31: resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} dependencies: - '@types/node': 20.11.20 - '@types/qs': 6.9.11 + '@types/node': 20.11.28 + '@types/qs': 6.9.12 '@types/range-parser': 1.2.7 dev: true /@types/express-serve-static-core@4.17.43: resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==} dependencies: - '@types/node': 20.11.20 - '@types/qs': 6.9.11 + '@types/node': 20.11.28 + '@types/qs': 6.9.12 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 dev: true @@ -1206,7 +1206,7 @@ packages: dependencies: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 4.17.31 - '@types/qs': 6.9.11 + '@types/qs': 6.9.12 '@types/serve-static': 1.15.5 dev: true @@ -1215,7 +1215,7 @@ packages: dependencies: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 4.17.43 - '@types/qs': 6.9.11 + '@types/qs': 6.9.12 '@types/serve-static': 1.15.5 dev: true @@ -1257,8 +1257,8 @@ packages: resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} dev: true - /@types/node@20.11.20: - resolution: {integrity: sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==} + /@types/node@20.11.28: + resolution: {integrity: sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==} dependencies: undici-types: 5.26.5 dev: true @@ -1270,26 +1270,26 @@ packages: /@types/parse-github-url@1.0.3: resolution: {integrity: sha512-7sTbCVmSVzK/iAsHGIxoqiyAnqix9opZm68lOvaU6DBx9EQ9kHMSp0y7Criu2OCsZ9wDllEyCRU+LU4hPRxXUA==} dependencies: - '@types/node': 20.11.20 + '@types/node': 20.11.28 dev: true - /@types/qs@6.9.11: - resolution: {integrity: sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==} + /@types/qs@6.9.12: + resolution: {integrity: sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==} dev: true /@types/range-parser@1.2.7: resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} dev: true - /@types/semver@7.5.7: - resolution: {integrity: sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==} + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} dev: true /@types/send@0.17.4: resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} dependencies: '@types/mime': 1.3.5 - '@types/node': 20.11.20 + '@types/node': 20.11.28 dev: true /@types/serve-static@1.15.5: @@ -1297,15 +1297,15 @@ packages: dependencies: '@types/http-errors': 2.0.4 '@types/mime': 3.0.4 - '@types/node': 20.11.20 + '@types/node': 20.11.28 dev: true /@types/unist@2.0.10: resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} dev: true - /@typescript-eslint/eslint-plugin@7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.57.0)(typescript@5.3.3): - resolution: {integrity: sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==} + /@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 @@ -1316,25 +1316,25 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.0.2(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 7.0.2 - '@typescript-eslint/type-utils': 7.0.2(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/utils': 7.0.2(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 7.0.2 + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/scope-manager': 7.2.0 + '@typescript-eslint/type-utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/visitor-keys': 7.2.0 debug: 4.3.4 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 semver: 7.6.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - typescript: 5.3.3 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@7.0.2(eslint@8.57.0)(typescript@5.3.3): - resolution: {integrity: sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==} + /@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^8.56.0 @@ -1343,13 +1343,13 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 7.0.2 - '@typescript-eslint/types': 7.0.2 - '@typescript-eslint/typescript-estree': 7.0.2(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 7.0.2 + '@typescript-eslint/scope-manager': 7.2.0 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) + '@typescript-eslint/visitor-keys': 7.2.0 debug: 4.3.4 eslint: 8.57.0 - typescript: 5.3.3 + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true @@ -1362,16 +1362,16 @@ packages: '@typescript-eslint/visitor-keys': 6.21.0 dev: true - /@typescript-eslint/scope-manager@7.0.2: - resolution: {integrity: sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==} + /@typescript-eslint/scope-manager@7.2.0: + resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 7.0.2 - '@typescript-eslint/visitor-keys': 7.0.2 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/visitor-keys': 7.2.0 dev: true - /@typescript-eslint/type-utils@7.0.2(eslint@8.57.0)(typescript@5.3.3): - resolution: {integrity: sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==} + /@typescript-eslint/type-utils@7.2.0(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^8.56.0 @@ -1380,12 +1380,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 7.0.2(typescript@5.3.3) - '@typescript-eslint/utils': 7.0.2(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) + '@typescript-eslint/utils': 7.2.0(eslint@8.57.0)(typescript@5.4.2) debug: 4.3.4 eslint: 8.57.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - typescript: 5.3.3 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true @@ -1395,12 +1395,12 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/types@7.0.2: - resolution: {integrity: sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==} + /@typescript-eslint/types@7.2.0: + resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3): + /@typescript-eslint/typescript-estree@6.21.0(typescript@5.4.2): resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -1416,14 +1416,14 @@ packages: is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.6.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - typescript: 5.3.3 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree@7.0.2(typescript@5.3.3): - resolution: {integrity: sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==} + /@typescript-eslint/typescript-estree@7.2.0(typescript@5.4.2): + resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -1431,20 +1431,20 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 7.0.2 - '@typescript-eslint/visitor-keys': 7.0.2 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/visitor-keys': 7.2.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.6.0 - ts-api-utils: 1.2.1(typescript@5.3.3) - typescript: 5.3.3 + ts-api-utils: 1.3.0(typescript@5.4.2) + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.3.3): + /@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.4.2): resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -1452,10 +1452,10 @@ packages: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 - '@types/semver': 7.5.7 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.2) eslint: 8.57.0 semver: 7.6.0 transitivePeerDependencies: @@ -1463,18 +1463,18 @@ packages: - typescript dev: true - /@typescript-eslint/utils@7.0.2(eslint@8.57.0)(typescript@5.3.3): - resolution: {integrity: sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==} + /@typescript-eslint/utils@7.2.0(eslint@8.57.0)(typescript@5.4.2): + resolution: {integrity: sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^8.56.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 - '@types/semver': 7.5.7 - '@typescript-eslint/scope-manager': 7.0.2 - '@typescript-eslint/types': 7.0.2 - '@typescript-eslint/typescript-estree': 7.0.2(typescript@5.3.3) + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 7.2.0 + '@typescript-eslint/types': 7.2.0 + '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.4.2) eslint: 8.57.0 semver: 7.6.0 transitivePeerDependencies: @@ -1490,11 +1490,11 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@7.0.2: - resolution: {integrity: sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==} + /@typescript-eslint/visitor-keys@7.2.0: + resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 7.0.2 + '@typescript-eslint/types': 7.2.0 eslint-visitor-keys: 3.4.3 dev: true @@ -1502,38 +1502,38 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vitest/expect@1.3.1: - resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==} + /@vitest/expect@1.4.0: + resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} dependencies: - '@vitest/spy': 1.3.1 - '@vitest/utils': 1.3.1 + '@vitest/spy': 1.4.0 + '@vitest/utils': 1.4.0 chai: 4.4.1 dev: true - /@vitest/runner@1.3.1: - resolution: {integrity: sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==} + /@vitest/runner@1.4.0: + resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} dependencies: - '@vitest/utils': 1.3.1 + '@vitest/utils': 1.4.0 p-limit: 5.0.0 pathe: 1.1.2 dev: true - /@vitest/snapshot@1.3.1: - resolution: {integrity: sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==} + /@vitest/snapshot@1.4.0: + resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} dependencies: - magic-string: 0.30.7 + magic-string: 0.30.8 pathe: 1.1.2 pretty-format: 29.7.0 dev: true - /@vitest/spy@1.3.1: - resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==} + /@vitest/spy@1.4.0: + resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} dependencies: tinyspy: 2.2.1 dev: true - /@vitest/utils@1.3.1: - resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==} + /@vitest/utils@1.4.0: + resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 @@ -1672,7 +1672,7 @@ packages: graphql: 16.8.1 dev: true - /apollo-server-express@3.13.0(express@4.18.2)(graphql@16.8.1): + /apollo-server-express@3.13.0(express@4.18.3)(graphql@16.8.1): resolution: {integrity: sha512-iSxICNbDUyebOuM8EKb3xOrpIwOQgKxGbR2diSr4HP3IW8T3njKFOoMce50vr+moOCe1ev8BnLcw9SNbuUtf7g==} engines: {node: '>=12.0'} peerDependencies: @@ -1689,7 +1689,7 @@ packages: apollo-server-types: 3.8.0(graphql@16.8.1) body-parser: 1.20.2 cors: 2.8.5 - express: 4.18.2 + express: 4.18.3 graphql: 16.8.1 parseurl: 1.3.3 transitivePeerDependencies: @@ -1798,26 +1798,6 @@ packages: resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} dev: true - /body-parser@1.20.1: - resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.11.0 - raw-body: 2.5.1 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /body-parser@1.20.2: resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -1876,7 +1856,7 @@ packages: es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.2.4 - set-function-length: 1.2.1 + set-function-length: 1.2.2 dev: true /callsites@3.1.0: @@ -2304,7 +2284,7 @@ packages: eslint: 8.57.0 dev: true - /eslint-config-prisma@0.2.0(@typescript-eslint/eslint-plugin@7.0.2)(@typescript-eslint/parser@7.0.2)(eslint-plugin-deprecation@2.0.0)(eslint-plugin-only-warn@1.1.0)(eslint-plugin-prefer-arrow@1.2.3)(eslint-plugin-simple-import-sort@12.0.0)(eslint-plugin-tsdoc@0.2.17)(eslint@8.57.0): + /eslint-config-prisma@0.2.0(@typescript-eslint/eslint-plugin@7.2.0)(@typescript-eslint/parser@7.2.0)(eslint-plugin-deprecation@2.0.0)(eslint-plugin-only-warn@1.1.0)(eslint-plugin-prefer-arrow@1.2.3)(eslint-plugin-simple-import-sort@12.0.0)(eslint-plugin-tsdoc@0.2.17)(eslint@8.57.0): resolution: {integrity: sha512-ky6iBCU9jk4o/SqkUmIUwspXjTfDO/d4glQ1VHrkL/SBra+PZjzsrDVX3pdXZgDw81+NFVYEBJIL4+lBL/yMCw==} peerDependencies: '@typescript-eslint/eslint-plugin': ^6 @@ -2316,28 +2296,28 @@ packages: eslint-plugin-simple-import-sort: ^10.0 eslint-plugin-tsdoc: ^0.2 dependencies: - '@typescript-eslint/eslint-plugin': 7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.57.0)(typescript@5.3.3) - '@typescript-eslint/parser': 7.0.2(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0)(eslint@8.57.0)(typescript@5.4.2) + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.2) eslint: 8.57.0 eslint-config-prettier: 9.1.0(eslint@8.57.0) - eslint-plugin-deprecation: 2.0.0(eslint@8.57.0)(typescript@5.3.3) + eslint-plugin-deprecation: 2.0.0(eslint@8.57.0)(typescript@5.4.2) eslint-plugin-only-warn: 1.1.0 eslint-plugin-prefer-arrow: 1.2.3(eslint@8.57.0) eslint-plugin-simple-import-sort: 12.0.0(eslint@8.57.0) eslint-plugin-tsdoc: 0.2.17 dev: true - /eslint-plugin-deprecation@2.0.0(eslint@8.57.0)(typescript@5.3.3): + /eslint-plugin-deprecation@2.0.0(eslint@8.57.0)(typescript@5.4.2): resolution: {integrity: sha512-OAm9Ohzbj11/ZFyICyR5N6LbOIvQMp7ZU2zI7Ej0jIc8kiGUERXPNMfw2QqqHD1ZHtjMub3yPZILovYEYucgoQ==} peerDependencies: eslint: ^7.0.0 || ^8.0.0 typescript: ^4.2.4 || ^5.0.0 dependencies: - '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.2) eslint: 8.57.0 tslib: 2.6.2 - tsutils: 3.21.0(typescript@5.3.3) - typescript: 5.3.3 + tsutils: 3.21.0(typescript@5.4.2) + typescript: 5.4.2 transitivePeerDependencies: - supports-color dev: true @@ -2489,13 +2469,13 @@ packages: strip-final-newline: 3.0.0 dev: true - /express@4.18.2: - resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + /express@4.18.3: + resolution: {integrity: sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==} engines: {node: '>= 0.10.0'} dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.1 + body-parser: 1.20.2 content-disposition: 0.5.4 content-type: 1.0.5 cookie: 0.5.0 @@ -2694,7 +2674,7 @@ packages: function-bind: 1.1.2 has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.1 + hasown: 2.0.2 dev: true /get-port@7.0.0: @@ -2707,8 +2687,8 @@ packages: engines: {node: '>=16'} dev: true - /get-tsconfig@4.7.2: - resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + /get-tsconfig@4.7.3: + resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==} dependencies: resolve-pkg-maps: 1.0.0 dev: true @@ -2796,8 +2776,8 @@ packages: resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - /happy-dom@13.5.0: - resolution: {integrity: sha512-0XWOSvyc03Z2Ye+VGP5pN6fpwgKMy3a2d09RuGRqAR4TEaW0SCtw3upt7dLoKaYBFUO+JeuDJh366aDBat3OVQ==} + /happy-dom@13.8.6: + resolution: {integrity: sha512-Urcv2jvNel19QirWimOwYTW3slpEYGS8PLtzEwAlpTWpnKycXF8s0I7xUBK9fPvWAIc8uZf/CnV2cIwWE8jptw==} engines: {node: '>=16.0.0'} dependencies: entities: 4.5.0 @@ -2840,8 +2820,8 @@ packages: engines: {node: '>= 0.4'} dev: true - /hasown@2.0.1: - resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==} + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} dependencies: function-bind: 1.1.2 @@ -2950,7 +2930,7 @@ packages: /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: - hasown: 2.0.1 + hasown: 2.0.2 dev: true /is-decimal@1.0.4: @@ -3183,8 +3163,8 @@ packages: engines: {node: '>=12'} dev: true - /magic-string@0.30.7: - resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} + /magic-string@0.30.8: + resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -3447,7 +3427,7 @@ packages: acorn: 8.11.3 pathe: 1.1.2 pkg-types: 1.0.3 - ufo: 1.4.0 + ufo: 1.5.1 dev: true /ms@2.0.0: @@ -3718,7 +3698,7 @@ packages: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} engines: {node: '>=0.6'} dependencies: - side-channel: 1.0.5 + side-channel: 1.0.6 dev: true /qs@6.5.3: @@ -3735,16 +3715,6 @@ packages: engines: {node: '>= 0.6'} dev: true - /raw-body@2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} - engines: {node: '>= 0.8'} - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - dev: true - /raw-body@2.5.2: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} @@ -3873,26 +3843,26 @@ packages: glob: 7.2.3 dev: true - /rollup@4.12.0: - resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==} + /rollup@4.13.0: + resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.12.0 - '@rollup/rollup-android-arm64': 4.12.0 - '@rollup/rollup-darwin-arm64': 4.12.0 - '@rollup/rollup-darwin-x64': 4.12.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.12.0 - '@rollup/rollup-linux-arm64-gnu': 4.12.0 - '@rollup/rollup-linux-arm64-musl': 4.12.0 - '@rollup/rollup-linux-riscv64-gnu': 4.12.0 - '@rollup/rollup-linux-x64-gnu': 4.12.0 - '@rollup/rollup-linux-x64-musl': 4.12.0 - '@rollup/rollup-win32-arm64-msvc': 4.12.0 - '@rollup/rollup-win32-ia32-msvc': 4.12.0 - '@rollup/rollup-win32-x64-msvc': 4.12.0 + '@rollup/rollup-android-arm-eabi': 4.13.0 + '@rollup/rollup-android-arm64': 4.13.0 + '@rollup/rollup-darwin-arm64': 4.13.0 + '@rollup/rollup-darwin-x64': 4.13.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.13.0 + '@rollup/rollup-linux-arm64-gnu': 4.13.0 + '@rollup/rollup-linux-arm64-musl': 4.13.0 + '@rollup/rollup-linux-riscv64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-musl': 4.13.0 + '@rollup/rollup-win32-arm64-msvc': 4.13.0 + '@rollup/rollup-win32-ia32-msvc': 4.13.0 + '@rollup/rollup-win32-x64-msvc': 4.13.0 fsevents: 2.3.3 dev: true @@ -3951,8 +3921,8 @@ packages: - supports-color dev: true - /set-function-length@1.2.1: - resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} dependencies: define-data-property: 1.1.4 @@ -3987,8 +3957,8 @@ packages: engines: {node: '>=8'} dev: true - /side-channel@1.0.5: - resolution: {integrity: sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==} + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 @@ -4163,13 +4133,13 @@ packages: resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} dev: true - /ts-api-utils@1.2.1(typescript@5.3.3): - resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==} + /ts-api-utils@1.3.0(typescript@5.4.2): + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.3.3 + typescript: 5.4.2 dev: true /tslib@1.14.1: @@ -4180,14 +4150,14 @@ packages: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true - /tsutils@3.21.0(typescript@5.3.3): + /tsutils@3.21.0(typescript@5.4.2): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 5.3.3 + typescript: 5.4.2 dev: true /tsx@4.7.1: @@ -4196,7 +4166,7 @@ packages: hasBin: true dependencies: esbuild: 0.19.12 - get-tsconfig: 4.7.2 + get-tsconfig: 4.7.3 optionalDependencies: fsevents: 2.3.3 dev: true @@ -4228,8 +4198,8 @@ packages: engines: {node: '>=10'} dev: true - /type-fest@4.10.3: - resolution: {integrity: sha512-JLXyjizi072smKGGcZiAJDCNweT8J+AuRxmPZ1aG7TERg4ijx9REl8CNhbr36RV4qXqL1gO1FF9HL8OkVmmrsA==} + /type-fest@4.12.0: + resolution: {integrity: sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==} engines: {node: '>=16'} dev: true @@ -4241,14 +4211,14 @@ packages: mime-types: 2.1.35 dev: true - /typescript@5.3.3: - resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + /typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} engines: {node: '>=14.17'} hasBin: true dev: true - /ufo@1.4.0: - resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==} + /ufo@1.5.1: + resolution: {integrity: sha512-HGyF79+/qZ4soRvM+nHERR2pJ3VXDZ/8sL1uLahdgEDf580NkgiWOxLk33FetExqOWp352JZRsgXbG/4MaGOSg==} dev: true /underscore@1.13.6: @@ -4372,8 +4342,8 @@ packages: vfile-message: 2.0.4 dev: true - /vite-node@1.3.1(@types/node@20.11.20): - resolution: {integrity: sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==} + /vite-node@1.4.0(@types/node@20.11.28): + resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true dependencies: @@ -4381,7 +4351,7 @@ packages: debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.1.4(@types/node@20.11.20) + vite: 5.1.6(@types/node@20.11.28) transitivePeerDependencies: - '@types/node' - less @@ -4393,8 +4363,8 @@ packages: - terser dev: true - /vite@5.1.4(@types/node@20.11.20): - resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==} + /vite@5.1.6(@types/node@20.11.28): + resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -4421,23 +4391,23 @@ packages: terser: optional: true dependencies: - '@types/node': 20.11.20 + '@types/node': 20.11.28 esbuild: 0.19.12 postcss: 8.4.35 - rollup: 4.12.0 + rollup: 4.13.0 optionalDependencies: fsevents: 2.3.3 dev: true - /vitest@1.3.1(@types/node@20.11.20)(happy-dom@13.5.0): - resolution: {integrity: sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==} + /vitest@1.4.0(@types/node@20.11.28)(happy-dom@13.8.6): + resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.3.1 - '@vitest/ui': 1.3.1 + '@vitest/browser': 1.4.0 + '@vitest/ui': 1.4.0 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -4454,27 +4424,27 @@ packages: jsdom: optional: true dependencies: - '@types/node': 20.11.20 - '@vitest/expect': 1.3.1 - '@vitest/runner': 1.3.1 - '@vitest/snapshot': 1.3.1 - '@vitest/spy': 1.3.1 - '@vitest/utils': 1.3.1 + '@types/node': 20.11.28 + '@vitest/expect': 1.4.0 + '@vitest/runner': 1.4.0 + '@vitest/snapshot': 1.4.0 + '@vitest/spy': 1.4.0 + '@vitest/utils': 1.4.0 acorn-walk: 8.3.2 chai: 4.4.1 debug: 4.3.4 execa: 8.0.1 - happy-dom: 13.5.0 + happy-dom: 13.8.6 local-pkg: 0.5.0 - magic-string: 0.30.7 + magic-string: 0.30.8 pathe: 1.1.2 picocolors: 1.0.0 std-env: 3.7.0 strip-literal: 2.0.0 tinybench: 2.6.0 tinypool: 0.8.2 - vite: 5.1.4(@types/node@20.11.20) - vite-node: 1.3.1(@types/node@20.11.20) + vite: 5.1.6(@types/node@20.11.28) + vite-node: 1.4.0(@types/node@20.11.28) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -4553,8 +4523,8 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /xss@1.0.14: - resolution: {integrity: sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==} + /xss@1.0.15: + resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} engines: {node: '>= 0.10.0'} hasBin: true dependencies: From a3b8b2f63678f057a462f55e3d67645215c29348 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 16 Mar 2024 23:14:08 -0400 Subject: [PATCH 68/71] format --- src/lib/graphql.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/graphql.ts b/src/lib/graphql.ts index 67a29cc66..d987b4f06 100644 --- a/src/lib/graphql.ts +++ b/src/lib/graphql.ts @@ -61,8 +61,7 @@ export const getTypeMapByKind = (schema: GraphQLSchema) => { return typeMapByKind } -export type ClassToName = - C extends GraphQLScalarType ? `GraphQLScalarType` +export type ClassToName = C extends GraphQLScalarType ? `GraphQLScalarType` : C extends GraphQLObjectType ? `GraphQLObjectType` : C extends GraphQLInterfaceType ? `GraphQLInterfaceType` : C extends GraphQLUnionType ? `GraphQLUnionType` From f65e07a765f914c86cecef5b8b2d12cf30cbe82c Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 16 Mar 2024 23:16:18 -0400 Subject: [PATCH 69/71] foramt --- tests/builder/_/schema.ts | 298 ++++++++++++++++++++------------------ 1 file changed, 158 insertions(+), 140 deletions(-) diff --git a/tests/builder/_/schema.ts b/tests/builder/_/schema.ts index 786783b61..3c795cd83 100644 --- a/tests/builder/_/schema.ts +++ b/tests/builder/_/schema.ts @@ -1,35 +1,36 @@ import type * as _ from '../../../src/Schema/__.js' export namespace $ { -export interface Index { -Root: { -Query: Root.Query -Mutation: null -Subscription: null -} -objects: { -Foo: Object.Foo -Bar: Object.Bar -ObjectNested: Object.ObjectNested -lowerCaseObject: Object.lowerCaseObject -lowerCaseObject2: Object.lowerCaseObject2 -Object: Object.Object -Object1ImplementingInterface: Object.Object1ImplementingInterface -Object2ImplementingInterface: Object.Object2ImplementingInterface -} -unions: { -Union: Union.FooBarUnion -| Union.lowerCaseUnion -} -scalars: Scalars -} -export interface Scalars { -ID: string -String: string -Int: number -Float: number -Boolean: boolean -} + export interface Index { + Root: { + Query: Root.Query + Mutation: null + Subscription: null + } + objects: { + Foo: Object.Foo + Bar: Object.Bar + ObjectNested: Object.ObjectNested + lowerCaseObject: Object.lowerCaseObject + lowerCaseObject2: Object.lowerCaseObject2 + Object: Object.Object + Object1ImplementingInterface: Object.Object1ImplementingInterface + Object2ImplementingInterface: Object.Object2ImplementingInterface + } + unions: { + Union: + | Union.FooBarUnion + | Union.lowerCaseUnion + } + scalars: Scalars + } + export interface Scalars { + ID: string + String: string + Int: number + Float: number + Boolean: boolean + } } // ------------------------------------------------------------ // @@ -37,51 +38,69 @@ Boolean: boolean // ------------------------------------------------------------ // export namespace Root { -export type Query = _.Object<"Query", { -interface: _.Field<_.Nullable> -id: _.Field<_.Nullable<_.Scalar.ID>> -idNonNull: _.Field<_.Scalar.ID> -string: _.Field<_.Nullable<_.Scalar.String>> -stringWithRequiredArg: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ -string: _.Scalar.String -}>> -stringWithArgs: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ -string: _.Nullable<_.Scalar.String> -int: _.Nullable<_.Scalar.Int> -float: _.Nullable<_.Scalar.Float> -boolean: _.Nullable<_.Scalar.Boolean> -id: _.Nullable<_.Scalar.ID> -}>> -stringWithArgEnum: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ -ABCEnum: _.Nullable -}>> -stringWithListArg: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ -ints: _.Nullable<_.List<_.Nullable<_.Scalar.Int>>> -}>> -stringWithListArgRequired: _.Field<_.Nullable<_.Scalar.String>, _.Args<{ -ints: _.List<_.Nullable<_.Scalar.Int>> -}>> -object: _.Field<_.Nullable> -listListIntNonNull: _.Field<_.List<_.List<_.Scalar.Int>>> -listListInt: _.Field<_.Nullable<_.List<_.Nullable<_.List<_.Nullable<_.Scalar.Int>>>>>> -listInt: _.Field<_.Nullable<_.List<_.Nullable<_.Scalar.Int>>>> -listIntNonNull: _.Field<_.List<_.Scalar.Int>> -objectNested: _.Field<_.Nullable> -objectNonNull: _.Field -objectWithArgs: _.Field<_.Nullable, _.Args<{ -string: _.Nullable<_.Scalar.String> -int: _.Nullable<_.Scalar.Int> -float: _.Nullable<_.Scalar.Float> -boolean: _.Nullable<_.Scalar.Boolean> -id: _.Nullable<_.Scalar.ID> -}>> -fooBarUnion: _.Field<_.Nullable> -/** -* Query enum field documentation. -*/ -abcEnum: _.Field<_.Nullable> -lowerCaseUnion: _.Field<_.Nullable> -}> + export type Query = _.Object<'Query', { + interface: _.Field<_.Nullable> + id: _.Field<_.Nullable<_.Scalar.ID>> + idNonNull: _.Field<_.Scalar.ID> + string: _.Field<_.Nullable<_.Scalar.String>> + stringWithRequiredArg: _.Field< + _.Nullable<_.Scalar.String>, + _.Args<{ + string: _.Scalar.String + }> + > + stringWithArgs: _.Field< + _.Nullable<_.Scalar.String>, + _.Args<{ + string: _.Nullable<_.Scalar.String> + int: _.Nullable<_.Scalar.Int> + float: _.Nullable<_.Scalar.Float> + boolean: _.Nullable<_.Scalar.Boolean> + id: _.Nullable<_.Scalar.ID> + }> + > + stringWithArgEnum: _.Field< + _.Nullable<_.Scalar.String>, + _.Args<{ + ABCEnum: _.Nullable + }> + > + stringWithListArg: _.Field< + _.Nullable<_.Scalar.String>, + _.Args<{ + ints: _.Nullable<_.List<_.Nullable<_.Scalar.Int>>> + }> + > + stringWithListArgRequired: _.Field< + _.Nullable<_.Scalar.String>, + _.Args<{ + ints: _.List<_.Nullable<_.Scalar.Int>> + }> + > + object: _.Field<_.Nullable> + listListIntNonNull: _.Field<_.List<_.List<_.Scalar.Int>>> + listListInt: _.Field<_.Nullable<_.List<_.Nullable<_.List<_.Nullable<_.Scalar.Int>>>>>> + listInt: _.Field<_.Nullable<_.List<_.Nullable<_.Scalar.Int>>>> + listIntNonNull: _.Field<_.List<_.Scalar.Int>> + objectNested: _.Field<_.Nullable> + objectNonNull: _.Field + objectWithArgs: _.Field< + _.Nullable, + _.Args<{ + string: _.Nullable<_.Scalar.String> + int: _.Nullable<_.Scalar.Int> + float: _.Nullable<_.Scalar.Float> + boolean: _.Nullable<_.Scalar.Boolean> + id: _.Nullable<_.Scalar.ID> + }> + > + fooBarUnion: _.Field<_.Nullable> + /** + * Query enum field documentation. + */ + abcEnum: _.Field<_.Nullable> + lowerCaseUnion: _.Field<_.Nullable> + }> } // ------------------------------------------------------------ // @@ -89,15 +108,15 @@ lowerCaseUnion: _.Field<_.Nullable> // ------------------------------------------------------------ // export namespace Enum { -/** -* Enum documentation. -* -* Members -* "A" - (DEPRECATED: Enum value A is deprecated.) -* "B" - Enum B member documentation. -* "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) -*/ -export type ABCEnum = _.Enum<"ABCEnum", ["A", "B", "C"] > + /** + * Enum documentation. + * + * Members + * "A" - (DEPRECATED: Enum value A is deprecated.) + * "B" - Enum B member documentation. + * "C" - Enum C member documentation. (DEPRECATED: Enum value C is deprecated.) + */ + export type ABCEnum = _.Enum<'ABCEnum', ['A', 'B', 'C']> } // ------------------------------------------------------------ // @@ -105,8 +124,7 @@ export type ABCEnum = _.Enum<"ABCEnum", ["A", "B", "C"] > // ------------------------------------------------------------ // export namespace InputObject { -// -- no types -- - + // -- no types -- } // ------------------------------------------------------------ // @@ -114,9 +132,9 @@ export namespace InputObject { // ------------------------------------------------------------ // export namespace Interface { -export type Interface = _.Interface<"Interface", { -id: _.Field<_.Nullable<_.Scalar.ID>> -}, [Object.Object1ImplementingInterface, Object.Object2ImplementingInterface]> + export type Interface = _.Interface<'Interface', { + id: _.Field<_.Nullable<_.Scalar.ID>> + }, [Object.Object1ImplementingInterface, Object.Object2ImplementingInterface]> } // ------------------------------------------------------------ // @@ -124,52 +142,52 @@ id: _.Field<_.Nullable<_.Scalar.ID>> // ------------------------------------------------------------ // export namespace Object { -/** -* Object documentation. -*/ -export type Foo = _.Object<"Foo", { -/** -* Field documentation. -* -* @deprecated Field a is deprecated. -*/ -id: _.Field<_.Nullable<_.Scalar.ID>> -}> - -export type Bar = _.Object<"Bar", { -int: _.Field<_.Nullable<_.Scalar.Int>> -}> - -export type ObjectNested = _.Object<"ObjectNested", { -id: _.Field<_.Nullable<_.Scalar.ID>> -object: _.Field<_.Nullable> -}> - -export type lowerCaseObject = _.Object<"lowerCaseObject", { -id: _.Field<_.Nullable<_.Scalar.ID>> -}> - -export type lowerCaseObject2 = _.Object<"lowerCaseObject2", { -int: _.Field<_.Nullable<_.Scalar.Int>> -}> - -export type Object = _.Object<"Object", { -string: _.Field<_.Nullable<_.Scalar.String>> -int: _.Field<_.Nullable<_.Scalar.Int>> -float: _.Field<_.Nullable<_.Scalar.Float>> -boolean: _.Field<_.Nullable<_.Scalar.Boolean>> -id: _.Field<_.Nullable<_.Scalar.ID>> -}> - -export type Object1ImplementingInterface = _.Object<"Object1ImplementingInterface", { -id: _.Field<_.Nullable<_.Scalar.ID>> -int: _.Field<_.Nullable<_.Scalar.Int>> -}> - -export type Object2ImplementingInterface = _.Object<"Object2ImplementingInterface", { -id: _.Field<_.Nullable<_.Scalar.ID>> -boolean: _.Field<_.Nullable<_.Scalar.Boolean>> -}> + /** + * Object documentation. + */ + export type Foo = _.Object<'Foo', { + /** + * Field documentation. + * + * @deprecated Field a is deprecated. + */ + id: _.Field<_.Nullable<_.Scalar.ID>> + }> + + export type Bar = _.Object<'Bar', { + int: _.Field<_.Nullable<_.Scalar.Int>> + }> + + export type ObjectNested = _.Object<'ObjectNested', { + id: _.Field<_.Nullable<_.Scalar.ID>> + object: _.Field<_.Nullable> + }> + + export type lowerCaseObject = _.Object<'lowerCaseObject', { + id: _.Field<_.Nullable<_.Scalar.ID>> + }> + + export type lowerCaseObject2 = _.Object<'lowerCaseObject2', { + int: _.Field<_.Nullable<_.Scalar.Int>> + }> + + export type Object = _.Object<'Object', { + string: _.Field<_.Nullable<_.Scalar.String>> + int: _.Field<_.Nullable<_.Scalar.Int>> + float: _.Field<_.Nullable<_.Scalar.Float>> + boolean: _.Field<_.Nullable<_.Scalar.Boolean>> + id: _.Field<_.Nullable<_.Scalar.ID>> + }> + + export type Object1ImplementingInterface = _.Object<'Object1ImplementingInterface', { + id: _.Field<_.Nullable<_.Scalar.ID>> + int: _.Field<_.Nullable<_.Scalar.Int>> + }> + + export type Object2ImplementingInterface = _.Object<'Object2ImplementingInterface', { + id: _.Field<_.Nullable<_.Scalar.ID>> + boolean: _.Field<_.Nullable<_.Scalar.Boolean>> + }> } // ------------------------------------------------------------ // @@ -177,10 +195,10 @@ boolean: _.Field<_.Nullable<_.Scalar.Boolean>> // ------------------------------------------------------------ // export namespace Union { -/** -* Union documentation. -*/ -export type FooBarUnion = _.Union<"FooBarUnion",[Object.Foo, Object.Bar]> + /** + * Union documentation. + */ + export type FooBarUnion = _.Union<'FooBarUnion', [Object.Foo, Object.Bar]> -export type lowerCaseUnion = _.Union<"lowerCaseUnion",[Object.lowerCaseObject, Object.lowerCaseObject2]> -} \ No newline at end of file + export type lowerCaseUnion = _.Union<'lowerCaseUnion', [Object.lowerCaseObject, Object.lowerCaseObject2]> +} From 93b07aa1b81c11919e07dc2745940865eb90ba89 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 16 Mar 2024 23:39:18 -0400 Subject: [PATCH 70/71] test client --- package.json | 6 ++++ src/SelectionSet/_.ts | 2 ++ src/SelectionSet/__.ts | 2 +- src/client.test.ts | 14 +++++++++ src/client.ts | 65 +++++++++++++++++++++++++++++----------- src/entrypoints/alpha.ts | 1 + 6 files changed, 72 insertions(+), 18 deletions(-) create mode 100644 src/SelectionSet/_.ts create mode 100644 src/client.test.ts create mode 100644 src/entrypoints/alpha.ts diff --git a/package.json b/package.json index 92f06e35b..23f5800ae 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,12 @@ "types": "./build/entrypoints/main.d.ts", "default": "./build/entrypoints/main.js" } + }, + "./alpha": { + "import": { + "types": "./build/entrypoints/alpha.d.ts", + "default": "./build/entrypoints/alpha.js" + } } }, "packageManager": "pnpm@8.15.4", diff --git a/src/SelectionSet/_.ts b/src/SelectionSet/_.ts new file mode 100644 index 000000000..fadcf1c32 --- /dev/null +++ b/src/SelectionSet/_.ts @@ -0,0 +1,2 @@ +export * from './SelectionSet.js' +export * from './toGraphQLDocumentString.js' diff --git a/src/SelectionSet/__.ts b/src/SelectionSet/__.ts index ff647a1f3..d927f56c3 100644 --- a/src/SelectionSet/__.ts +++ b/src/SelectionSet/__.ts @@ -1 +1 @@ -export * as SelectionSet from './SelectionSet.js' +export * as SelectionSet from './_.js' diff --git a/src/client.test.ts b/src/client.test.ts new file mode 100644 index 000000000..3a502dc4a --- /dev/null +++ b/src/client.test.ts @@ -0,0 +1,14 @@ +import { expect, test } from 'vitest' +import type { $ } from '../tests/builder/_/schema.js' +import { setupMockServer } from '../tests/legacy/__helpers.js' +import { create } from './client.js' + +const ctx = setupMockServer() +const data = { fooBarUnion: { int: 1 } } + +test(`query`, async () => { + const mockRes = ctx.res({ body: { data } }).spec.body! + // @ts-expect-error ignoreme + const client = create<$.Index>({ url: ctx.url }) + expect(await client.query({ fooBarUnion: { onBar: { int: true } } })).toEqual(mockRes.data) +}) diff --git a/src/client.ts b/src/client.ts index af368abd0..574a9ae95 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,26 +1,57 @@ +import request from './entrypoints/main.js' import type { ResultSet } from './ResultSet/__.js' -import type { Index } from './Schema/__.js' -import type { SelectionSet } from './SelectionSet/__.js' +import type { Schema } from './Schema/__.js' +import { SelectionSet } from './SelectionSet/__.js' // dprint-ignore -export type Client<$SchemaIndex extends Index> = - & ($SchemaIndex['Root']['Query'] extends null ? { - query: <$SelectionSet extends SelectionSet.Query<$SchemaIndex>>(selectionSet: $SelectionSet) => Promise> - } - : unknown) - & ($SchemaIndex['Root']['Mutation'] extends null ? { - mutation: <$SelectionSet extends SelectionSet.Mutation<$SchemaIndex>>(selectionSet: $SelectionSet) => Promise> - } - : unknown) - & ($SchemaIndex['Root']['Subscription'] extends null ? { - subscription: <$SelectionSet extends SelectionSet.Subscription<$SchemaIndex>>(selectionSet: $SelectionSet) => Promise> - } - : unknown) +export type Client<$SchemaIndex extends Schema.Index> = + & ( + $SchemaIndex['Root']['Query'] extends null + ? unknown + : { + query: <$SelectionSet extends SelectionSet.Query<$SchemaIndex>>(selectionSet: $SelectionSet) => Promise> + } + ) + & ( + $SchemaIndex['Root']['Mutation'] extends null + ? unknown + : { + mutation: <$SelectionSet extends SelectionSet.Mutation<$SchemaIndex>>(selectionSet: $SelectionSet) => Promise> + } + ) +// todo +// & ($SchemaIndex['Root']['Subscription'] extends null ? { +// subscription: <$SelectionSet extends SelectionSet.Subscription<$SchemaIndex>>(selectionSet: $SelectionSet) => Promise> +// } +// : unknown) interface Input { url: URL | string + headers?: HeadersInit } -export const create = <$SchemaIndex extends Index>(input: Input): Client<$SchemaIndex> => { - return input as any +export const create = <$SchemaIndex extends Schema.Index>(input: Input): Client<$SchemaIndex> => { + // @ts-expect-error ignoreme + const client: Client<$SchemaIndex> = { + query: async (documentQueryObject: any) => { + const documentQueryString = SelectionSet.toGraphQLDocumentString(documentQueryObject) + return await request({ + url: new URL(input.url).href, + requestHeaders: input.headers, + document: documentQueryString, + }) + }, + mutation: async (documentMutationObject: any) => { + const documentMutationString = SelectionSet.toGraphQLDocumentString(documentMutationObject) + return await request({ + url: new URL(input.url).href, + requestHeaders: input.headers, + document: documentMutationString, + }) + }, + // todo + // subscription: async () => { + // }, + } + return client } diff --git a/src/entrypoints/alpha.ts b/src/entrypoints/alpha.ts new file mode 100644 index 000000000..f07fac449 --- /dev/null +++ b/src/entrypoints/alpha.ts @@ -0,0 +1 @@ +export * from '../client.js' From 4adc8b37e2c264d3fa5b277c72ac6ff40eaf7ed0 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 16 Mar 2024 23:41:51 -0400 Subject: [PATCH 71/71] fix --- src/Schema/Field/Field.ts | 2 +- src/client.test.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Schema/Field/Field.ts b/src/Schema/Field/Field.ts index c4923f413..83912f8f1 100644 --- a/src/Schema/Field/Field.ts +++ b/src/Schema/Field/Field.ts @@ -42,7 +42,7 @@ export const field = <$Type extends Type.Any, $Args extends null | Args = null>( } } -export type Field<$Type extends Type.Any = Type.Any, $Args extends Args | null = Args | null> = { +export type Field<$Type extends any = any, $Args extends Args | null = Args | null> = { typeUnwrapped: Type.Unwrap<$Type> type: $Type args: $Args diff --git a/src/client.test.ts b/src/client.test.ts index 3a502dc4a..234eb883b 100644 --- a/src/client.test.ts +++ b/src/client.test.ts @@ -8,7 +8,8 @@ const data = { fooBarUnion: { int: 1 } } test(`query`, async () => { const mockRes = ctx.res({ body: { data } }).spec.body! - // @ts-expect-error ignoreme + // eslint-disable-next-line + // @ts-ignore infinite depth const client = create<$.Index>({ url: ctx.url }) expect(await client.query({ fooBarUnion: { onBar: { int: true } } })).toEqual(mockRes.data) })