UNPKG

grafast

Version:

Cutting edge GraphQL planning and execution engine

278 lines 10.7 kB
import type { FragmentDefinitionNode, GraphQLField, GraphQLObjectType, GraphQLOutputType, GraphQLSchema, GraphQLUnionType, OperationDefinitionNode } from "graphql"; import { $$timeout, $$ts } from "../constants.js"; import type { Constraint } from "../constraints.js"; import type { ErrorBehavior, GrafastPlanJSON } from "../index.js"; import { Step } from "../index.js"; import type { FieldPlanResolver, LocationDetails, TrackedArguments } from "../interfaces.js"; import type { ApplyAfterModeArg } from "../operationPlan-input.js"; import type { GrafastOperationOptions } from "../prepare.js"; import type { LayerPlanReasonSubroutine } from "./LayerPlan.js"; import { LayerPlan } from "./LayerPlan.js"; import type { ProcessGroupedFieldSetDetails, StreamDetails } from "./OperationPlanTypes.js"; import { OutputPlan } from "./OutputPlan.js"; export declare const POLYMORPHIC_ROOT_PATH: null; export declare const POLYMORPHIC_ROOT_PATHS: ReadonlySet<string> | null; /** Beware: the list of phases may change over time... @experimental */ export type OperationPlanPhase = "init" | "plan" | "validate" | "optimize" | "finalize" | "ready"; export interface MetaByMetaKey { [metaKey: string | number | symbol]: Record<string, any>; } export declare class OperationPlan { readonly schema: GraphQLSchema; readonly operation: OperationDefinitionNode; readonly fragments: { [fragmentName: string]: FragmentDefinitionNode; }; readonly variableValues: { [key: string]: any; }; readonly context: { [key: string]: any; }; readonly rootValue: any; readonly errorBehavior: ErrorBehavior; readonly [$$timeout]: undefined; readonly [$$ts]: undefined; readonly queryType: GraphQLObjectType; readonly mutationType: GraphQLObjectType | null; readonly subscriptionType: GraphQLObjectType | null; readonly unionsContainingObjectType: { [objectTypeName: string]: ReadonlyArray<GraphQLUnionType>; }; private operationType; /** * What state is the OpPlan in? * * 1. init * 2. plan * 3. validate * 5. optimize * 6. finalize * 7. ready * * Once in 'ready' state we can execute the plan. */ phase: OperationPlanPhase; /** * Gets updated as we work our way through the plan, useful for making errors more helpful. */ loc: string[] | null; /** * If true, then this operation doesn't use (custom) resolvers. */ pure: boolean; private startTime; private previousLap; private laps; private optimizeMeta; private scalarPlanInfo; private frozenPlanningPaths; private readonly planningTimeout; private readonly maxPlanningDepth; constructor(schema: GraphQLSchema, operation: OperationDefinitionNode, fragments: { [fragmentName: string]: FragmentDefinitionNode; }, variableValuesConstraints: Constraint[], variableValues: { [key: string]: any; }, contextConstraints: Constraint[], context: { [key: string]: any; }, rootValueConstraints: Constraint[], rootValue: any, errorBehavior: ErrorBehavior, options: GrafastOperationOptions); private lap; private checkTimeout; private planOperation; /** * Plans a GraphQL query operation. */ private planQuery; /** * Implements the `PlanOpPlanMutation` algorithm. */ private planMutation; /** * Implements the `PlanOpPlanSubscription` algorithm. */ private planSubscription; private getCombinedLayerPlanForLayerPlans; /** * Gets the item plan for a given parent list plan - this ensures we only * create one item plan per parent plan. */ private itemStepForListStep; processGroupedFieldSet(details: ProcessGroupedFieldSetDetails): Generator<() => { haltTree: boolean; step: Step; latestSideEffectStep: Step | null; }, void, { step: Step<any>; haltTree: boolean; latestSideEffectStep: Step<any> | null; }>; /** * * @param outputPlan - The output plan that this selection set is being added to * @param path - The path within the outputPlan that we're adding stuff (only for root/object OutputPlans) * @param parentStep - The step that represents the selection set root * @param objectType - The object type that this selection set is being evaluated for (note polymorphic selection should already have been handled by this point) * @param selections - The GraphQL selections (fields, fragment spreads, inline fragments) to evaluate * @param isMutation - If true this selection set should be executed serially rather than in parallel (each field gets its own LayerPlan) */ private planSelectionSet; private planningQueue; private planningQueueByPlanningPath; private queueNextLayer; private planPending; withLocLpSE(loc: string[] | null, layerPlan: LayerPlan, layerPlanLatestSideEffect: Step | null, cb: () => void): void; private mutateTodos; private internalDependency; private planFieldReturnType; private planIntoOutputPlan; handlePlanningError(details: { outputPlan: OutputPlan; layerPlan: LayerPlan; objectType: GraphQLObjectType | null; responseKey: string | null; positionType: GraphQLOutputType; locationDetails: LocationDetails; err: Error; }): void; private polymorphicPlanObjectType; planFieldBatch: PlanFieldBatch | null; private batchPlanField; private processPlanField; _realPlanField(planFieldDetails: PlanFieldDetails): { step: Step<unknown>; haltTree: boolean; latestSideEffectStep: Step<any> | null; } | { step: import("../index.js").ErrorStep<any>; haltTree: boolean; latestSideEffectStep: Step<any> | null; }; /** * A replacement for GraphQL's * `CoerceArgumentValues` that factors in tracked variables. * * @see https://spec.graphql.org/draft/#CoerceArgumentValues() */ private getTrackedArguments; /** * Sets up tracking for the given value (variableValues, context, rootValue). */ private track; /** * Checks that no step has a property on it whose value is another step. It * should addDependency instead. */ private validateSteps; private replaceStep; private processStep; /** * Peers are steps of the same type (but not the same step!) that are in * compatible layers and have the same dependencies. Peers must not have side * effects. A step is not its own peer. */ private getPeers; private isImmoveable; /** * Attempts to hoist the step into a higher layerPlan to maximize * deduplication. */ private hoistStep; /** * Attempts to push the step into the lowest layerPlan to minimize the need * for copying between layer plans. */ private pushDown; private _deduplicateInnerLogic; private deduplicateStep; private deduplicateStepsProcess; /** * Gives us a chance to replace nearly-duplicate plans with other existing * plans (and adding the necessary transforms); this means that by the time * we come to optimize the plan tree should already be simpler. For example * if you have two plans at the same level that both request row data from * the same database table with the same identifiers, `WHERE`, `LIMIT`, * `OFFSET` and `ORDER BY`, but different `SELECT`s we could merge the two * plans together by replacing the latter with the former and having the * former SELECT additional fields, then transform the results back to what * our child plans would be expecting. */ private deduplicateSteps; private _deduplicateStepsInner; private hoistAndDeduplicate; private hoistSteps; private pushDownSteps; /** * Calls the 'optimize' method on a plan, which may cause the plan to * communicate with its (deep) dependencies, and even to replace itself with * a different plan. */ private optimizeStep; /** * Note that we work through dependents first so we can make sure that we * know all our dependent's needs before we optimise ourself. */ private optimizeSteps; private inlineSteps; /** Finalizes each step */ private finalizeSteps; private finalizeLayerPlans; private finalize; /** Optimizes each output plan */ private optimizeOutputPlans; /** Finalizes each output plan */ private finalizeOutputPlans; private walkOutputPlans; generatePlanJSON(): GrafastPlanJSON; finishSubroutine(subroutineStep: Step, layerPlan: LayerPlan<LayerPlanReasonSubroutine>): void; getStepsByMetaKey(metaKey: string | number | symbol): Step[]; getStepsByStepClass<TClass extends Step>(klass: { new (...args: any[]): TClass; }): TClass[]; private _cacheStepStoreByActionKeyByLayerPlan; private _immutableCacheStepStoreAndActionKey; /** * Cache a generated step by a given identifier (cacheKey) such that we don't * need to regenerate it on future calls, significantly reducing the load on * deduplication later. * * @experimental */ cacheStep<T extends Step>(ownerStep: Step, actionKey: string, cacheKey: symbol | string | number | boolean | null | undefined, cb: () => T): T; _cacheStep<T extends Step>(store: StepCache, ownerStep: Step, actionKey: string, cacheKey: any, cb: () => T): T; /** * Clears the cache, typically due to side effects having taken place. Called * from setting hasSideEffects on an ExecutableStep, among other places. */ resetCache(): void; withRootLayerPlan<T>(cb: () => T): T; } type StepCache = Record<string, Map<any, any> | undefined>; interface PlanFieldDetails { typeName: string; fieldName: string; layerPlan: LayerPlan; path: readonly string[]; polymorphicPaths: ReadonlySet<string> | null; planningPath: string; planResolver: FieldPlanResolver; applyAfterMode: ApplyAfterModeArg; rawParentStep: Step; field: GraphQLField<any, any>; trackedArguments: TrackedArguments; streamDetails: StreamDetails | true | false | null; } type PlanFieldBatchResult = { error: Error; } | { error?: never; haltTree: boolean; step: Step; latestSideEffectStep: Step | null; }; interface PlanFieldBatch { complete: boolean; batch: Array<PlanFieldDetails>; results: Array<PlanFieldBatchResult>; } export {}; //# sourceMappingURL=OperationPlan.d.ts.map