Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions grafast/website/grafast/flow.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,15 @@ object identifiers ("Node IDs"), optional foreign keys, or other advanced
scenarios where you need to suppress downstream work or turn those suppressions
back into useful data.

:::danger[Inhibition is not a security boundary]

Inhibition enables Gra*fast* to optimize away work that is unnecessary for a
particular result. It does not guarantee that the work will not execute. Never
use inhibition for authorization or to prevent security-sensitive operations;
enforce those requirements inside the protected operation or data source.

:::

If you do reach for them, a common sequence is to guard an input, inhibit
downstream work when that guard fails, and optionally trap the inhibition later
so the field can return a benign value:
Expand Down Expand Up @@ -327,8 +336,8 @@ flowchart TD
/>

Here `inhibitOnNull` marks only the `null` entries as inhibited, so `loadOne`
never attempts to fetch those users while the rest of the batch proceeds as
normal. Other helpers build on the same idea:
can avoid fetching those users while the rest of the batch proceeds as normal.
Other helpers build on the same idea:

- [`inhibitOnNull`](./standard-steps/inhibitOnNull.mdx) skips
dependent work for `null` inputs while still returning `null` to the caller.
Expand All @@ -346,5 +355,6 @@ should not depend on the specific step class. In plan diagrams the step is
usually absorbed into the dependency edge, so you will see labels such as
`rejectNull`, `trapError`, or `onReject="…"` rather than a dedicated node.
Crucially, Gra*fast* applies these flags per entry: if one item in a batch is
inhibited or errored it is simply omitted from the `execute()` call while the
rest of the items carry on unhindered.
inhibited or errored, Gra*fast* can omit it from the `execute()` call while the
rest of the items carry on unhindered. This is an optimization and must not be
relied on for correctness or security.
6 changes: 3 additions & 3 deletions grafast/website/grafast/plan-resolvers/best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ import { loadOne, trap, inhibitOnNull, TRAP_ERROR } from "grafast";
function post_author_plan($post) {
const $authorId = $post.get("authorId");

// Guard against null authorId — skip the load entirely
// Allow Gra*fast* to skip the load when authorId is null
const $guardedId = inhibitOnNull($authorId);

// Load the author; if it errors, convert to null
Expand All @@ -359,8 +359,8 @@ function post_author_plan($post) {

The key flow control steps are:

- [`inhibitOnNull()`](../standard-steps/inhibitOnNull.mdx) — suppresses
downstream work when a value is `null`
- [`inhibitOnNull()`](../standard-steps/inhibitOnNull.mdx) — enables
Gra*fast* to suppress downstream work when a value is `null`
- [`assertNotNull()`](../standard-steps/assertNotNull.mdx) — turns
`null` into a `SafeError` visible to clients
- [`trap()`](../standard-steps/trap.mdx) — recovers inhibited or errored
Expand Down
14 changes: 12 additions & 2 deletions grafast/website/grafast/standard-steps/inhibitIf.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import Mermaid from "@theme/Mermaid";
Returns a step that yields the same values as the passed in step, but
downstream work is inhibited whenever the condition step yields `true`.

:::danger[Not a security boundary]

`inhibitIf` enables Gra*fast* to optimize away unnecessary downstream work; it
does not guarantee that the work will not execute. Do not use it for
authorization or to prevent security-sensitive operations. Enforce those
requirements inside the protected operation or data source.

:::

Unlike [`inhibitOnNull`](./inhibitOnNull.mdx), `inhibitIf` is an explicit step
in the plan, so it will typically appear in plan diagrams as its own node.

Expand All @@ -29,8 +38,9 @@ return trap($result, TRAP_INHIBITED, {
```

In the example above, when `$isEmpty` yields `true`, the dependent work is
inhibited and Gra*fast* omits the affected entries from downstream execution.
When the condition yields `false`, the wrapped step behaves as a pass-through.
inhibited and Gra*fast* can omit the affected entries from downstream
execution. When the condition yields `false`, the wrapped step behaves as a
pass-through.

## Plan diagrams

Expand Down
9 changes: 9 additions & 0 deletions grafast/website/grafast/standard-steps/inhibitOnEmpty.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
Returns a step that yields the same values as the passed in step, but
downstream work is inhibited when the yielded value is considered empty.

:::danger[Not a security boundary]

`inhibitOnEmpty` enables Gra*fast* to optimize away unnecessary downstream
work; it does not guarantee that the work will not execute. Do not use it for
authorization or to prevent security-sensitive operations. Enforce those
requirements inside the protected operation or data source.

:::

Gra*fast* currently considers a value empty if it is:

- `undefined`
Expand Down
17 changes: 12 additions & 5 deletions grafast/website/grafast/standard-steps/inhibitOnNull.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import Mermaid from "@theme/Mermaid";
Returns a step that yields the same values as the passed in step, but downstream
work is inhibited when a yielded value is `null` or `undefined`.

:::danger[Not a security boundary]

`inhibitOnNull` enables Gra*fast* to optimize away unnecessary downstream work;
it does not guarantee that the work will not execute. Do not use it for
authorization or to prevent security-sensitive operations. Enforce those
requirements inside the protected operation or data source.

:::

:::note[Declarative flow]

The inhibition only affects steps that depend on the wrapper step (and those
Expand Down Expand Up @@ -36,16 +45,14 @@ Should all indices be inhibited, the step will not execute at all.

```ts
const $parentId = get($post, "parentId");
// If $parentId is null, the `loadOne` will be inhibited (will not execute)
// If $parentId is null, Gra*fast* can avoid executing `loadOne`
const $parent = loadOne(inhibitOnNull($parentId), batchGetPostById);
```

In the example above, when the `$parentId` step yields `null`, the associated
value for the `loadOne` step will automatically yield `null` (and be inhibited)
without needing to execute. Only the uninhibited non-null values pass through to
the `loadOne` step for execution. If all values for a step are inhibited then the
step will not execute at all. When a value for `$parentId` is not nullish,
execution for that value proceeds as normal.
and Gra*fast* can avoid executing the load for that entry. When a value for
`$parentId` is not nullish, execution for that value proceeds as normal.

## Trapping

Expand Down
Loading