grafast
Version:
Cutting edge GraphQL planning and execution engine
278 lines • 10.7 kB
TypeScript
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