Skip to content

feat: improve server action UI update scheduling and add Prettier - #6

Merged
ivogt merged 1 commit into
mainfrom
queuing-updates
Nov 30, 2025
Merged

ivogt merged 1 commit into
mainfrom
queuing-updates

Conversation

@ivogt

@ivogt ivogt commented Nov 30, 2025

Copy link
Copy Markdown
Collaborator

Summary

  • Improved server action UI update scheduling in server-action-bridge.ts using queueMicrotask + requestAnimationFrame instead of requestIdleCallback to properly yield to React's synchronous work
  • Added Prettier as a dev dependency with VSCode configuration
  • Updated @vitejs/plugin-rsc to 0.5.2

Test plan

  • Verify server actions still update UI correctly after execution
  • Test that action state commits before UI updates

🤖 Generated with Claude Code

feat: add Prettier configuration and update dependencies
@ivogt
ivogt merged commit fe691e5 into main Nov 30, 2025
ivogt added a commit that referenced this pull request Jun 13, 2026
… decisions, #570 cleanup (#573)

* test(router): dual-mode e2e for prerender+loading()+passthrough (M16) and redirect under loading() (M9)

Adds the dev + production e2e fixtures the M16/M9 unit-tested fixes still
lacked (CLAUDE.md mandates dual-mode coverage). Deferred from the stabilization
PR; addresses the two main gaps in #567.

- prerender-loading: Prerender + loading() + ctx.passthrough(). The loading()
  boundary defers the build handler so the passthrough sentinel arrives
  Promise-wrapped; detectPrerenderPassthrough awaits it. getParams lists
  [baked, skip]: baked is prerendered, skip passes through to the live handler.
  Production asserts the manifest holds baked but NOT skip (M16 regression
  guard: count === 1; without the fix skip would bake a corrupt artifact).
- loading-redirect: a synchronous redirect() return under loading()
  short-circuits to a real HTTP redirect. Verified for hard + soft navigation
  in both modes.

Regenerated router.named-routes.gen.ts for the two new includes (urls.gen.ts is
legacy/unreferenced and no longer auto-generated, so left untouched).

Lower-priority #567 gaps (build-time intercept _currentSegmentId handle drop,
server-action-bridge pre-dispatch over-invalidation, document-cache
private+s-maxage) need fixes first and are deferred; #567 stays open for them.

* chore(router): safe cleanup batch from review (#570)

Internal-only, behavior-preserving cleanups; grep-verified safe and gate-green
(typecheck, full unit suite, lint, format).

- Remove write-only MiddlewareEntry.mountPrefix (read nowhere; scoping folds
  into fullPattern before the entry is built). Dropped 3 production assignments
  plus the test fixtures.
- Dedupe cookie helpers: delete the test-only middleware-cookies.ts (byte-for-byte
  dup of request-context's parseCookiesFromHeader/serializeCookieValue), export
  the canonical copies from request-context for the unit tests, drop the
  middleware.ts re-export.
- Delete the zero-importer src/handles/index.ts barrel (Meta/MetaTags/Breadcrumbs
  are exported directly from the client/index entries); repoint collect-handle.test.
- Drop the MiddlewareCollectableEntry re-export from middleware.ts; its one
  importer now imports from middleware-types.
- Extract hasContextVars() into context-var.ts and use it in the two prerender
  entry points that duplicated the keys-or-symbols predicate.
- renderStaticSegment: throw a TypeError when a Static handler returns a Response
  (was silently serialized as a corrupt build artifact); the fresh/revalidation
  paths already throw via handleHandlerResult. Adds a unit test.
- ctx.reverse() middleware fallback: widen the signature to (name, params?, search?)
  to match ScopedReverseFunction and make the error message accurate.
- docs: tree-structure.md inTransitionScope also covers error/notFound segments.

Deferred, still open in #570: the optionalParams all-or-nothing removal and the
loaderPromises param removal both sit on matrix-critical matching/segment-resolution
paths and warrant a semantic-matrix run rather than a cleanup sweep. The
document-cache "timing-gap caveat" cited at document-cache.ts:45-53 does not exist
there (those lines are Cache-Control parsing), so that doc item is a no-op.

* feat(router): pre-1.0 public-API surface decisions from review (#569 part 1)

Type/naming/export-surface decisions (no behavior change). See #569.

- #1 router.use(pattern, mw): add a pattern-generic overload so
  `.use("/users/:id", (ctx) => ...)` types `ctx.params.id` via ExtractParams.
  Bare `.use(mw)` stays generic. Added on both Rango and RangoInternal. Pins it
  with a type-level test (router-use-types.test.ts).
- #2 useAction: export a stable public `ActionState` (and `ActionLifecycleState`)
  from ./client so consumers can name the hook's return type; TrackedActionState
  stays the @internal impl name (ActionState aliases it).
- #4 buildEnv: move off the shared base onto per-preset interfaces. Cloudflare
  keeps `"auto"` (wrangler getPlatformProxy); Node now rejects `"auto"` at the
  type level (Exclude<BuildEnvOption,"auto">) instead of only at build time.
- #5 poke plugin: rename name "vite-plugin-poke" -> "@rangojs/router:poke" so it
  matches the @rangojs/router:* family for consumers ordering/filtering by name.
- #6 ./build: move realm-crossing pipeline internals (buildRouteTrie,
  buildPerRouterTrie, TrieNode, TrieLeaf, collectFallbackClientRefs) to a new
  ./build/internal subpath; ./build keeps the documented public API
  (generateManifest*, route-type generation, createScanFilter, hashParams).
- #8 mergeHandlerUse: document the intentional validation asymmetry (explicit
  use() items bypass the mount-site allow-list); chosen over enforcing it.

Deferred to part 2 (behavior changes): #3 prefetch options, #7 scanFilter
wiring, #9 profile-registry trim, #10 redirect-in-action idiom + examples.

* feat(router): prefetch source-scope option + redirect-in-action ergonomics (#569 part 2a)

- #3 router.prefetch(url, { key: ":source" }): add an options arg so imperative
  prefetch can source-scope its cache entry, at parity with
  <Link prefetchKey=":source">. Wired through to prefetchDirect.
- #10 redirect() in actions: drop the `as any` on non-form actions by typing
  them Promise<Response> (saveAndRedirect, actionSimpleRedirect). The form-bound
  peReturnRedirect keeps a documented intrinsic cast (React's form-action type is
  () => void | Promise<void>, which can't express a Response return); consumers
  should prefer `throw redirect(...)`. Strengthened the server-actions skill.

Deferred to part 2b: #7 (complete scanFilter — needs a new public plugin option)
and #9 (trim profile-registry — runtime cache-resolution refactor).

* feat(router): complete scanFilter discovery option + trim profile registry (#569 part 2b)

- #7 complete the scanFilter feature. Adds a public rango() option
  `discovery: { include, exclude }` (glob, root-relative; mirrors the CLI's
  --include/--exclude). The consume side was already wired
  (findRouterFiles(state.projectRoot, state.scanFilter) at 3 sites); this
  threads the option through PluginOptions and compiles it via createScanFilter
  into state.scanFilter in configResolved, once projectRoot is known. Filters
  which files route discovery / static type-gen scan. Tests in
  generate-route-types.test.ts (include restricts, exclude removes).
- #9 trim the profile registry. Removed the global mutable `_profiles`,
  `setCacheProfiles`, and `getCacheProfile` — all dead: `getCacheProfile` had no
  production reader (its own JSDoc said so), and `setCacheProfiles` only
  populated the unread global. Kept the load-bearing `resolveCacheProfiles`,
  which createRouter() already uses to thread a per-request profile map (the
  "use cache: <profile>" path reads it request-scoped via ctx._cacheProfiles).
  Dropped the router.ts setCacheProfiles() call; repointed the tests to
  resolveCacheProfiles (and reframed the "global replace" test as per-router
  map independence).

#569 is now fully implemented (all 10 decisions).

* fix(router): keep build internals on ./build (revert #569.6) + throw in return-form redirect actions (#569.10)

CI on #573 caught a regression from #569 part 1: the Vite discovery pipeline
imports buildRouteTrie / buildPerRouterTrie / collectFallbackClientRefs at build
time via runner.import("@rangojs/router/build") (discover-routers.ts), and the
RSC runner does not resolve a ./build/internal subpath in that realm. Moving them
left collectFallbackClientRefs undefined, so the router-level default error /
notFound boundary was never collected into the app-fallback chunk
(mini.test.ts clientChunks: "mini-default-error must be in app-fallback").

Revert #569.6: restore buildRouteTrie/buildPerRouterTrie/collectFallbackClientRefs
+ TrieNode/TrieLeaf on ./build; delete ./build/internal + its package.json export.
#569.6 is re-deferred — internalizing these needs runner subpath resolution or a
different seam (noted in build/index.ts).

#569.10 revision: per owner, the return-form redirect test actions
(saveAndRedirect, actionSimpleRedirect) now `throw redirect()` instead of being
typed Promise<Response>. The form-bound peReturnRedirect keeps its documented
intrinsic cast.

Verified: mini clientChunks 6/6, location-state e2e 47/47 (dev+prod), typecheck,
3866 unit, lint, format.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant