Skip to content

Commit 122af7e

Browse files
committed
Add branded dep ID docs
1 parent 6e389bd commit 122af7e

4 files changed

Lines changed: 50 additions & 41 deletions

File tree

grafast/grafast/examples/complexInputs.mts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ setTimeout(async () => {
6262
// Everything below this line will be output in the docs at
6363
// http://build.graphile.org/graphile-build/next/all-hooks
6464
/******************************************************************************/
65-
import type { ExecutionDetails, ExecutionValue, Maybe } from "grafast";
65+
import type { DepId, ExecutionDetails, Maybe } from "grafast";
6666
import { makeGrafastSchema, Modifier, Step } from "grafast";
6767

6868
interface Filterable {
@@ -117,8 +117,8 @@ class SearchRequestStep extends Step<{
117117
sql: string;
118118
patchJSON: string;
119119
}> {
120-
private readonly patchDepId: number;
121-
private readonly applyDepIds: number[] = [];
120+
private readonly patchDepId: DepId<Step<BakedUserPatch>>;
121+
private readonly applyDepIds: DepId<Step<FilterCallbacks>>[] = [];
122122

123123
constructor($patch: Step<BakedUserPatch>) {
124124
super();
@@ -130,17 +130,15 @@ class SearchRequestStep extends Step<{
130130
}
131131

132132
execute(details: ExecutionDetails) {
133-
const patchDep = details.values[
134-
this.patchDepId
135-
] as ExecutionValue<BakedUserPatch>;
133+
const patchDep = details.values.at(this.patchDepId);
136134

137135
const clauses: string[] = [
138136
// Put any initial clauses here
139137
];
140138

141139
// Apply the callbacks
142140
const applyCallbacks = this.applyDepIds
143-
.flatMap((id) => details.values[id].unaryValue() as FilterCallbacks)
141+
.flatMap((id) => details.values.at(id).unaryValue())
144142
.filter((cb) => cb != null);
145143
const filterable: Filterable = {
146144
addClause: (clause) => void clauses.push(clause),

grafast/website/grafast/index.mdx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ The GraphQL specification
8585
[notes](https://spec.graphql.org/draft/#sec-Conforming-Algorithms):
8686

8787
> _Conformance requirements [...] can be fulfilled [...] in any way as long as
88-
> the perceived result is equivalent._
88+
> the perceived result is equivalent._
8989
> https://spec.graphql.org/draft/#sec-Conforming-Algorithms
9090
9191
Gra*fast* has been written very carefully by a [GraphQL Technical Steering
@@ -336,9 +336,10 @@ once all of its dependencies are ready, continuing until all steps are
336336
complete.
337337

338338
At planning time a step can add a dependency on another step via `const depId =
339-
this.addDependency($otherStep);`. This `depId` is the index in the **values
340-
tuple** that the step can use at execution time to retrieve the associated
341-
values.
339+
this.addDependency($otherStep);`. This `depId` is a branded dependency ID: at
340+
runtime it is the index in the **values tuple**, and in TypeScript it carries
341+
the dependency step type so the step can use `values.at(depId)` at execution
342+
time to retrieve the associated values.
342343

343344
When a step executes, its `execute` method is passed the **execution
344345
details** which includes:
@@ -393,7 +394,7 @@ export class RecordsByColumnStep extends Step {
393394
super();
394395
this.tableName = tableName;
395396
this.columnName = columnName;
396-
this.columnValueDepIdx = this.addDependency($columnValue);
397+
this.columnValueDepId = this.addDependency($columnValue);
397398
}
398399

399400
setFirst($first) {
@@ -402,15 +403,15 @@ export class RecordsByColumnStep extends Step {
402403

403404
async execute({ indexMap, values }) {
404405
// Retrieve the values for the `$columnValue` dependency
405-
const columnValueDep = values[this.columnValueDepIdx];
406+
const columnValueDep = values.at(this.columnValueDepId);
406407

407408
// We may or may not have added a `$first` limit:
408409
const firstDep =
409-
this.firstDepId !== undefined ? values[this.firstDepId] : undefined;
410+
this.firstDepId !== undefined ? values.at(this.firstDepId) : undefined;
410411

411412
// firstDep, if it exists, is definitely a unary dep (!firstDep.isBatch), so
412413
// we can retrieve its value directly:
413-
const first = firstDep ? parseInt(firstDep.value, 10) : null;
414+
const first = firstDep ? parseInt(firstDep.unaryValue(), 10) : null;
414415

415416
// Create a `LIMIT` clause in our SQL if the user specified a `$first` limit:
416417
const limitSQL = Number.isFinite(first) ? `limit ${first}` : ``;

grafast/website/grafast/plan-resolvers/complex-inputs.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,10 @@ may apply to the same step, you should expect multiple calls to `.apply()`
241241
and thus store all dependency IDs in an array:
242242

243243
```ts
244+
import { Step, type DepId } from "grafast";
245+
244246
class MyRequestStep extends Step {
245-
applyDepIds: number[] = [];
247+
applyDepIds: DepId<Step<(parent: any) => void>>[] = [];
246248

247249
apply($cb: Step<(parent: any) => void>) {
248250
this.applyDepIds.push(this.addUnaryDependency($cb));
@@ -273,7 +275,7 @@ class MyRequestStep extends Step {
273275

274276
// Apply the changes from all the `.apply($cb)` calls
275277
for (const applyDepId of this.applyDepIds) {
276-
const applyCallback = values[applyDepId].unaryValue();
278+
const applyCallback = values.at(applyDepId).unaryValue();
277279
applyCallback(builder);
278280
}
279281

grafast/website/grafast/step-classes.mdx

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,10 @@ but Gra*fast* steps are much more powerful thanks to the additional dependencies
131131
and lifecycle methods.
132132

133133
When the step class adds a dependency (with `this.addDependency($step)` or
134-
similar) a dependency index, `depIndex`, is returned. Dependency indexes start
135-
at 0 and increase monotonically (a dependency can never be removed[^1]).
134+
similar) a dependency ID, `depId`, is returned. Dependency IDs are numbers at
135+
runtime and start at 0, increasing monotonically (a dependency can never be
136+
removed[^1]). In TypeScript they are branded as `DepId<TStep>`, which preserves
137+
the dependency step type for helpers such as `details.values.at(depId)`.
136138

137139
[^1]:
138140
To remove a dependency, you must instead replace the step with a copy that
@@ -155,9 +157,10 @@ of the execute method.
155157

156158
`details.values` is also critical: an ordered array of execution values, one for
157159
each dependency. You can retrieve the execution value for a given dependency via
158-
its `depIndex` as `const depEv = details.values[depIndex]`. Once you have an
159-
execution value, you can retrieve the value for a given index in the batch via
160-
`const value = depEv.at(batchIndex)`.
160+
its `depId` as `const depEv = details.values.at(depId)`. Tuple destructuring or
161+
`details.values[depIndex]` is also fine when it is more convenient. Once you
162+
have an execution value, you can retrieve the value for a given index in the
163+
batch via `const value = depEv.at(batchIndex)`.
161164

162165
Gra*fast* tracks which steps will always represent exactly one value (e.g. the
163166
GraphQL context, input values passed as field arguments, constants, etc), and
@@ -180,9 +183,9 @@ top of the execute method:
180183
class MyStep extends Step {
181184
constructor($a, $b, $c) {
182185
super();
183-
this.aDepIndex = this.addUnaryDependency($a);
184-
this.bDepIndex = this.addDependency($b);
185-
this.cDepIndex = this.addDependency($c);
186+
this.addUnaryDependency($a);
187+
this.addDependency($b);
188+
this.addDependency($c);
186189
}
187190
execute(details) {
188191
const { values } = details;
@@ -243,23 +246,25 @@ Here's a hypothetical example, concentrate on the `execute()` method and see how
243246
the steps outlined above play out:
244247

245248
```ts
249+
import { Step, type DepId } from "grafast";
246250
import { languageService } from "./services/language";
247251
248-
class TranslationStep extends Step {
249-
langDepIndex: number;
250-
textDepIndex: number;
252+
class TranslationStep extends Step<string | undefined> {
253+
langDepId: DepId<Step<string>>;
254+
textDepId: DepId<Step<string>>;
255+
251256
constructor($language: Step<string>, $text: Step<string>) {
252257
super();
253258
// Add our dependencies
254-
this.langDepIndex = this.addDependency($language);
255-
this.textDepIndex = this.addDependency($text);
259+
this.langDepId = this.addDependency($language);
260+
this.textDepId = this.addDependency($text);
256261
}
257-
execute(details) {
262+
async execute(details) {
258263
// highlight-start
259264
// 1. Extract and identify the execution value for each dependency:
260265
// highlight-end
261-
const langEv = details.values[this.langDepIndex];
262-
const textEv = details.values[this.textDepIndex];
266+
const langEv = details.values.at(this.langDepId);
267+
const textEv = details.values.at(this.textDepId);
263268
264269
// highlight-start
265270
// 2. Map over the indices to prepare the input for our translation API:
@@ -278,7 +283,7 @@ class TranslationStep extends Step {
278283
// highlight-start
279284
// 4. Finally, return results that correlate with the inputs:
280285
// highlight-end
281-
return indexMap((batchIndex) => {
286+
return details.indexMap((batchIndex) => {
282287
const language = langEv.at(batchIndex);
283288
const sourceText = textEv.at(batchIndex);
284289
const match = translations.find(
@@ -736,8 +741,10 @@ as part of `Step`.
736741
737742
When your step requires another step's value in order to execute (which is the
738743
case for the majority of steps!) it must add a dependency via the
739-
`this.addDependency($otherStep)` method. This method will return a number,
740-
which is the index in the `execute` values tuple that represents this step.
744+
`this.addDependency($otherStep)` method. This method will return a `DepId`
745+
(a branded number), which is the index in the `execute` values tuple that
746+
represents the dependency. TypeScript uses the brand to infer the dependency's
747+
data type when you later call `details.values.at(depId)`.
741748
742749
It's common to do this in the constructor, but it can be done at other stages
743750
too, for example during the optimize phase a step's descendant might ask it to
@@ -750,8 +757,8 @@ class added two dependencies:
750757
class AddStep extends Step {
751758
constructor($a, $b) {
752759
super();
753-
this.addDependency($a); // Returns 0
754-
this.addDependency($b); // Returns 1
760+
this.addDependency($a); // Returns 0 as a DepId
761+
this.addDependency($b); // Returns 1 as a DepId
755762
}
756763
}
757764
```
@@ -805,9 +812,10 @@ input values are not).
805812
806813
### getDep
807814
808-
Pass in the number of the dependency (`0` for the first dependency, `1` for the
809-
second, and so on) and Gra*fast* will return the corresponding step. This should
810-
only be used before or during the `optimize` phase.
815+
Pass in the dependency ID returned from `addDependency`, or a literal dependency
816+
index (`0` for the first dependency, `1` for the second, and so on), and
817+
Gra*fast* will return the corresponding step. This should only be used before
818+
or during the `optimize` phase.
811819
812820
For example in the `AddStep` example above we might have:
813821

0 commit comments

Comments
 (0)