Skip to content

Commit

Permalink
Be stricter about types and usage of getDep/getDepOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
benjie committed Feb 26, 2025
1 parent d06e474 commit 3b670d8
Show file tree
Hide file tree
Showing 14 changed files with 111 additions and 50 deletions.
13 changes: 8 additions & 5 deletions grafast/dataplan-pg/src/steps/pgPolymorphic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,13 @@ export class PgPolymorphicStep<
}) as any;
}

itemPlan(): TItemStep {
return this.getDep<any>(this.itemStepId);
private itemPlan(): TItemStep {
return this.getDepOptions<TItemStep>(this.itemStepId).step;
}

typeSpecifierPlan(): TTypeSpecifierStep {
return this.getDep<TTypeSpecifierStep>(this.typeSpecifierStepId);
private typeSpecifierPlan(): TTypeSpecifierStep {
return this.getDepOptions<TTypeSpecifierStep>(this.typeSpecifierStepId)
.step;
}

planForType(type: GraphQLObjectType): ExecutableStep {
Expand All @@ -101,7 +102,9 @@ export class PgPolymorphicStep<
).join("', '")}'`,
);
}
return spec.plan(this.typeSpecifierPlan(), this.itemPlan());
const $typeSpecifier = this.typeSpecifierPlan();
const $item = this.itemPlan();
return spec.plan($typeSpecifier, $item);
}

private getTypeNameFromSpecifier(specifier: TTypeSpecifier) {
Expand Down
2 changes: 1 addition & 1 deletion grafast/dataplan-pg/src/steps/pgSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1644,7 +1644,7 @@ export class PgSelectRowsStep<
}

public getClassStep(): PgSelectStep<TResource> {
return this.getDep<PgSelectStep<TResource>>(0);
return this.getDepOptions<PgSelectStep<TResource>>(0).step;
}

listItem(itemPlan: Step) {
Expand Down
2 changes: 1 addition & 1 deletion grafast/dataplan-pg/src/steps/pgUnionAll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1096,7 +1096,7 @@ export class PgUnionAllRowsStep<
this.addDependency($pgUnionAll);
}
public getClassStep(): PgUnionAllStep<TAttributes, TTypeNames> {
return this.getDep<PgUnionAllStep<TAttributes, TTypeNames>>(0);
return this.getDepOptions<PgUnionAllStep<TAttributes, TTypeNames>>(0).step;
}

listItem(itemPlan: Step) {
Expand Down
2 changes: 1 addition & 1 deletion grafast/grafast/src/engine/OutputPlan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ export class OutputPlan<TType extends OutputPlanType = OutputPlanType> {
| undefined = ($root.unbatchedExecute! as any)[expressionSymbol];
if (expressionDetails !== undefined) {
// @ts-ignore
const $parent: Step = $root.getDep(0);
const { step: $parent } = $root.getDepOptions(0);
this.layerPlan.operationPlan.stepTracker.setOutputPlanRootStep(
this,
$parent,
Expand Down
15 changes: 11 additions & 4 deletions grafast/grafast/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,7 @@ export interface LocationDetails {
}

export type UnwrapPlanTuple</* const */ TIn extends readonly Step[]> = {
[Index in keyof TIn]: TIn[Index] extends Step<infer U> ? U : never;
[Index in keyof TIn]: DataFromStep<TIn[Index]>;
};

export type NotVariableValueNode = Exclude<ValueNode, VariableNode>;
Expand All @@ -809,14 +809,21 @@ export type Maybe<T> = T | null | undefined;

export * from "./planJSONInterfaces.js";

export interface AddDependencyOptions {
step: Step;
export interface AddDependencyOptions<TStep extends Step = Step> {
step: TStep;
skipDeduplication?: boolean;
/** @defaultValue `FLAG_NULL` */
acceptFlags?: ExecutionEntryFlags;
onReject?: null | Error | undefined;
onReject?: Maybe<Error>;
nonUnaryMessage?: ($dependent: Step, $dependency: Step) => string;
}

export interface DependencyOptions<TStep extends Step = Step> {
step: TStep;
acceptFlags: ExecutionEntryFlags;
onReject: Maybe<Error>;
}

/**
* @internal
*/
Expand Down
42 changes: 35 additions & 7 deletions grafast/grafast/src/step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getDebug } from "./global.js";
import { inspect } from "./inspect.js";
import type {
AddDependencyOptions,
DependencyOptions,
ExecutionDetails,
ExecutionEntryFlags,
ExecutionResults,
Expand All @@ -36,7 +37,7 @@ import {
ALL_FLAGS,
DEFAULT_FORBIDDEN_FLAGS,
} from "./interfaces.js";
import type { __ItemStep } from "./steps/index.js";
import type { __FlagStep, __ItemStep } from "./steps/index.js";
import { stepADependsOnStepB, stepAMayDependOnStepB } from "./utils.js";

/**
Expand Down Expand Up @@ -167,8 +168,11 @@ export /* abstract */ class Step<TData = any> {
/**
* What execution entry flags we can't handle for the given indexed dependency
* (default = this.defaultForbiddenFlags)
*
* @internal
*/
protected readonly dependencyForbiddenFlags: ReadonlyArray<ExecutionEntryFlags>;
/** @internal */
protected readonly dependencyOnReject: ReadonlyArray<
Error | null | undefined
>;
Expand Down Expand Up @@ -347,25 +351,49 @@ export /* abstract */ class Step<TData = any> {
return this.layerPlan.getStep(id, this);
}

protected getDepOptions(depId: number): AddDependencyOptions {
const step = this.dependencies[depId];
protected getDepOptions<TStep extends Step = Step>(
depId: number,
): DependencyOptions<TStep> {
const step = this.dependencies[depId] as TStep;
const forbiddenFlags = this.dependencyForbiddenFlags[depId];
const onReject = this.dependencyOnReject[depId];
const acceptFlags = ALL_FLAGS & ~forbiddenFlags;
return { step, acceptFlags, onReject };
}

protected getDep<T extends Step = Step>(_depId: number): T {
protected getDep<TStep extends Step = Step>(
_depId: number,
): TStep | __FlagStep<TStep>;
protected getDep<TStep extends Step = Step>(
_depId: number,
throwOnFlagged: true,
): TStep;
protected getDep<TStep extends Step = Step>(
_depId: number,
_throwOnFlagged = false,
): TStep | __FlagStep<TStep> {
// This gets replaced when `__FlagStep` is loaded. Were we on ESM we could
// just put the code here, but since we're not we have to avoid the
// circular dependency.
throw new Error(`Grafast failed to load correctly`);
}

protected maybeGetDep<T extends Step = Step>(
protected maybeGetDep<TStep extends Step = Step>(
depId: number | null | undefined,
): TStep | __FlagStep<TStep> | null;
protected maybeGetDep<TStep extends Step = Step>(
depId: number | null | undefined,
throwOnFlagged: true,
): TStep | null;
protected maybeGetDep<TStep extends Step = Step>(
depId: number | null | undefined,
): T | null {
return depId == null ? null : this.getDep<T>(depId);
throwOnFlagged = false,
): TStep | __FlagStep<TStep> | null {
return depId == null
? null
: throwOnFlagged
? this.getDep<TStep>(depId, true)
: this.getDep<TStep>(depId);
}

protected getDepOrConstant<TData = any>(
Expand Down
46 changes: 30 additions & 16 deletions grafast/grafast/src/steps/__flag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { $$inhibit, flagError, SafeError } from "../error.js";
import { inspect } from "../inspect.js";
import type {
AddDependencyOptions,
DataFromStep,
ExecutionDetails,
ExecutionEntryFlags,
GrafastResultsList,
Expand Down Expand Up @@ -89,7 +90,7 @@ function resolveTrapValue(tv: TrapValue): ResolvedTrapValue {
}
}

export class __FlagStep<TData> extends Step<TData> {
export class __FlagStep<TStep extends Step> extends Step<DataFromStep<TStep>> {
static $$export = {
moduleName: "grafast",
exportName: "__FlagStep",
Expand All @@ -102,7 +103,7 @@ export class __FlagStep<TData> extends Step<TData> {
private valueForInhibited: ResolvedTrapValue;
private valueForError: ResolvedTrapValue;
private canBeInlined: boolean;
constructor(step: Step, options: FlagStepOptions) {
constructor(step: TStep, options: FlagStepOptions) {
super();
const {
acceptFlags = DEFAULT_ACCEPT_FLAGS,
Expand Down Expand Up @@ -207,8 +208,8 @@ export class __FlagStep<TData> extends Step<TData> {
}

public execute(
_details: ExecutionDetails<[data: TData, cond?: boolean]>,
): GrafastResultsList<TData> {
_details: ExecutionDetails<[data: DataFromStep<TStep>, cond?: boolean]>,
): GrafastResultsList<DataFromStep<TStep>> {
throw new Error(`${this} not finalized?`);
}

Expand All @@ -222,7 +223,7 @@ export class __FlagStep<TData> extends Step<TData> {
}

private fancyExecute(
details: ExecutionDetails<[data: TData, cond?: boolean]>,
details: ExecutionDetails<[data: DataFromStep<TStep>, cond?: boolean]>,
): any {
const dataEv = details.values[0]!;
const condEv =
Expand Down Expand Up @@ -268,7 +269,7 @@ export class __FlagStep<TData> extends Step<TData> {

// Checks already performed via addDependency, just pass everything through. Should have been inlined!
private passThroughExecute(
details: ExecutionDetails<[data: TData, cond?: boolean]>,
details: ExecutionDetails<[data: DataFromStep<TStep>, cond?: boolean]>,
): any {
const ev = details.values[0];
if (ev.isBatch) {
Expand All @@ -284,11 +285,11 @@ export class __FlagStep<TData> extends Step<TData> {
* Example use case: get user by id, but id is null: no need to fetch the user
* since we know they won't exist.
*/
export function inhibitOnNull<T>(
$step: Step<T>,
export function inhibitOnNull<TStep extends Step>(
$step: TStep,
options?: { if?: FlagStepOptions["if"] },
) {
return new __FlagStep<T>($step, {
return new __FlagStep<TStep>($step, {
...options,
acceptFlags: DEFAULT_ACCEPT_FLAGS & ~FLAG_NULL,
});
Expand All @@ -299,39 +300,52 @@ export function inhibitOnNull<T>(
* that represents a Post instead: throw error to tell user they've sent invalid
* data.
*/
export function assertNotNull<T>(
$step: Step<T>,
export function assertNotNull<TStep extends Step>(
$step: TStep,
message: string,
options?: { if?: FlagStepOptions["if"] },
) {
return new __FlagStep<T>($step, {
return new __FlagStep<TStep>($step, {
...options,
acceptFlags: DEFAULT_ACCEPT_FLAGS & ~FLAG_NULL,
onReject: new SafeError(message),
});
}

export function trap<T>(
$step: Step<T>,
export function trap<TStep extends Step>(
$step: TStep,
acceptFlags: ExecutionEntryFlags,
options?: {
valueForInhibited?: FlagStepOptions["valueForInhibited"];
valueForError?: FlagStepOptions["valueForError"];
if?: FlagStepOptions["if"];
},
) {
return new __FlagStep<T>($step, {
return new __FlagStep<TStep>($step, {
...options,
acceptFlags: (acceptFlags & TRAPPABLE_FLAGS) | FLAG_NULL,
});
}

// Have to overwrite the getDep method due to circular dependency
(Step.prototype as any).getDep = function (this: Step, depId: number) {
(Step.prototype as any).getDep = function (
this: Step,
depId: number,
throwOnFlagged = false,
) {
const { step, acceptFlags, onReject } = this.getDepOptions(depId);
if (acceptFlags === DEFAULT_ACCEPT_FLAGS && onReject == null) {
return step;
} else {
if (throwOnFlagged) {
throw new Error(
`When retrieving dependency ${step} of ${this}, the dependency is flagged as ${digestAcceptFlags(
acceptFlags,
)}/onReject=${String(
onReject,
)}. Please use \`this.getDepOptions(depId)\` instead, and handle the flags`,
);
}
// Return a __FlagStep around options.step so that all the options are preserved.
return new __FlagStep(step, { acceptFlags, onReject });
}
Expand Down
2 changes: 1 addition & 1 deletion grafast/grafast/src/steps/__inputDynamicScalar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export class __InputDynamicScalarStep<
/** @internal */
eval(): TLeaf {
const variableValues = this.variableNames.map((variableName, i) =>
this.getDep<__TrackedValueStep>(i).eval(),
this.getDep<__TrackedValueStep>(i, true).eval(),
);
return this.valueFromValues(variableValues);
}
Expand Down
2 changes: 1 addition & 1 deletion grafast/grafast/src/steps/__trackedValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export class __TrackedValueStep<
}

private getValuePlan() {
return this.getDep<__ValueStep<TData> | AccessStep<TData>>(0);
return this.getDep<__ValueStep<TData> | AccessStep<TData>>(0, true);
}

/**
Expand Down
8 changes: 4 additions & 4 deletions grafast/grafast/src/steps/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,12 @@ export class AccessStep<TData> extends UnbatchedStep<TData> {
}

toStringMeta(): string {
return `${chalk.bold.yellow(String(this.getDep(0).id))}.${this.path
.map((p) => String(p))
.join(".")}`;
return `${chalk.bold.yellow(
String(this.getDepOptions(0).step.id),
)}.${this.path.map((p) => String(p)).join(".")}`;
}

getParentStep(): Step {
getParentStep() {
return this.getDep(0);
}

Expand Down
4 changes: 2 additions & 2 deletions grafast/grafast/src/steps/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export class ConnectionStep<
});
}
public getBefore(): TCursorStep | null {
return this.maybeGetDep<TCursorStep>(this._beforeDepId);
return this.maybeGetDep<TCursorStep>(this._beforeDepId, true);
}
public setBefore($beforePlan: Step<string | null | undefined>) {
if ($beforePlan instanceof ConstantStep && $beforePlan.data == null) {
Expand All @@ -203,7 +203,7 @@ export class ConnectionStep<
});
}
public getAfter(): TCursorStep | null {
return this.maybeGetDep<TCursorStep>(this._afterDepId);
return this.maybeGetDep<TCursorStep>(this._afterDepId, true);
}
public setAfter($afterPlan: Step<string | null | undefined>) {
if ($afterPlan instanceof ConstantStep && $afterPlan.data == null) {
Expand Down
8 changes: 6 additions & 2 deletions grafast/grafast/src/steps/listTransform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
import type { LayerPlanReasonSubroutine } from "../engine/LayerPlan.js";
import { LayerPlan } from "../engine/LayerPlan.js";
import { withGlobalLayerPlan } from "../engine/lib/withGlobalLayerPlan.js";
import type { ConnectionCapableStep, ExecutionDetails } from "../index.js";
import type {
__FlagStep,
ConnectionCapableStep,
ExecutionDetails,
} from "../index.js";
import type { GrafastResultsList } from "../interfaces.js";
import { $$deepDepSkip } from "../interfaces.js";
import type { ListCapableStep } from "../step.js";
Expand Down Expand Up @@ -186,7 +190,7 @@ export class __ListTransformStep<
}

getListStep(): TListStep {
return this.getDep<TListStep>(this.rawListStepDepId);
return this.getDepOptions<TListStep>(this.rawListStepDepId).step;
}

[$$deepDepSkip]() {
Expand Down
Loading

0 comments on commit 3b670d8

Please sign in to comment.