Repository navigation
docs(revalidate): canonical ctx.isAction idioms + shop-demo adoption - #559
Merged
Merged
Conversation
commit: |
Land the rename-safe revalidation recommendation where consumers look for it - the ShouldRevalidateFn JSDoc and the rango skill - and adopt it in the two pure-suppress shop demo predicates. The rest of the demo suite stays on actionId substring matching on purpose (see D1). D2 (the recommendation) - the ShouldRevalidateFn JSDoc and rango skill now teach two named idioms with ctx.isAction instead of one ambiguous example: Idiom A "mine, else defer" (ctx.isAction(X) || undefined) and Idiom B "mine only", bare ctx.isAction(X). Both are staleness-free: threading the optional `stale` into the suppress form (ctx.isAction(X) || ctx.stale) returns undefined - i.e. defer, not suppress - when stale is absent, so the canonical examples rely only on isAction's raw boolean. D1 (scoped to the clean wins) - the two pure-suppress shop predicates (CartLoader, ModalRecommendationsLoader) move to ctx.isAction(ShopActions) / ctx.isAction(addToCart, addToCartWithResult), which is both rename-safe and shorter. kanban/todos/loaders deliberately keep actionId?.includes(...): it is supported floor API (not a deprecated pattern), those fixtures keep exercising that path through real action flows, and the isAction rewrite there needed a verbose `ctx.actionId == null ? <fallback> : false` guard that read worse than the original. D3 - drop the redundant `: ResponseHandlerContext` annotation and its now-unused import from the cloudflare-basic robots.txt path.text handler; inline path.* callbacks are contextually typed, so inference is clean under strict:true. Verified: typecheck, unit (3562), lint, format, and the shop revalidation e2e (shop-actions + shop-loaders) in dev + production (22 passed).
ivogt
force-pushed
the
fix/dx-feedback-d1-d2-d3
branch
from
June 12, 2026 11:41
c4513cc to
7263e50
Compare
This was referenced Jun 12, 2026
ivogt
marked this pull request as ready for review
June 13, 2026 10:59
ivogt
added a commit
that referenced
this pull request
Jun 13, 2026
#559 (docs(revalidate): ctx.isAction idioms + shop-demo adoption) touched cloudflare-basic/src/urls.tsx. Sole conflict was the @rangojs/router import line: resolved to { urls, updateTag, revalidateTag } — dropping both `cookies` (this branch extracted the inline cookies().set into middleware/cookie-overlay.ts, so the body no longer uses it) and `ResponseHandlerContext` (#559 removed the `(ctx: ResponseHandlerContext)` annotation in favor of inference). Verified neither is referenced in the merged body. Verified: typecheck 12/12, test:unit:all (router 3816 unit + 45 RSC, cf 42, mini 16, demo 15), lint 0/0, format.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
ctx.isAction()is the rename-safe way to match server actions in a revalidate predicate, but the canonical guidance was thin and partly self-contradictory: theShouldRevalidateFnJSDoc taught a single ambiguous||-vs-??example, and the shop demo modeledactionId?.includes("Cart") ?? false— valid, but rename-fragile, and not what a consumer should reach for first.This PR lands the recommendation where consumers actually look (the JSDoc that shows on IDE hover + the rango skill) and adopts it in the two shop demo predicates where the conversion is a clean win. It deliberately does not sweep the rest of the suite — see Notes.
What a consumer sees differently
The hover doc on
revalidate(...)and the rango skill now teach two named idioms instead of one ambiguous example:ctx.isAction(X) || undefined(defer)ctx.isAction(X)(suppress)The first draft of the suppress idiom wrote
ctx.isAction(X) || ctx.stale; sincestale?: booleanis optional, that evaluates toundefined— i.e. defer, not suppress — wheneverstaleis absent. Fixed to the bare form, so the JSDoc and skill exemplars are now identical.Shop demo adoption (the clean wins)
Both shop sites are pure-suppress, so the conversion is exactly equivalent (verified by e2e).
Code-change summary
packages/rangojs-router/src/types/handler-context.ts—ShouldRevalidateFnJSDoc teaches the two named idioms withctx.isAction.packages/rangojs-router/skills/rango/SKILL.md— matching defer/suppress note.tests/vite-rsc-demo/src/urls/shop.tsx— 2 pure-suppress predicates →ctx.isAction(ShopActions)/ctx.isAction(addToCart, addToCartWithResult), + 1import * as ShopActions.tests/cloudflare-basic/src/urls.tsx— drop a redundant: ResponseHandlerContextannotation + its now-unused import (inlinepath.*callbacks are contextually typed; proven clean understrict:true).Test plan
shop-actions+shop-loaders, 22 passed (pins the keptisAction(ShopActions)cart-revalidation and shared-loader-freshness in both modes)Notes / deliberately not done
actionId?.includes(...).actionIdsubstring matching is supported floor API — the typesafety skill calls it the "floor and escape hatch" — not a deprecated pattern, so a demo using it isn't a hygiene violation. Those fixtures keep exercising that path through real action flows, and theisActionrewrite there needed a verbosectx.actionId == null ? <fallback> : falseguard that read worse than the original. The recommendation lives in the docs; the suite keeps its pattern variety.actionId?.includesin the defer form; the API path has dedicated e2e (action-id-resolution.test.ts,is-action.test.ts), and two snippets must stay substring regardless: the loader return-protocol section (built onactionId'sstring | undefinedshape —isActionwould make?? falsedead code) andserver-actions'startsWith("src/actions/account/")(matches unimported modules, whichisActioncannot express).