Skip to content

fix(files): read the Files prefetch from the data layer and bound invitation previews - #6415

Merged
waleedlatif1 merged 1 commit into
stagingfrom
fix/files-prefetch-and-invitation-previews
Aug 8, 2026
Merged

fix(files): read the Files prefetch from the data layer and bound invitation previews#6415
waleedlatif1 merged 1 commit into
stagingfrom
fix/files-prefetch-and-invitation-previews

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

Two unrelated load-time fixes, 8 files.

Files page (/workspace/[id]/files). On a hard refresh the folders painted first and the files a beat later. Both are prefetched and hydrated together, so the files entry was not reaching the client. Each read went to its own route over an internal HTTP request — prefetchQuery swallows a rejection and shouldDehydrateQuery drops the errored entry, so a failure there silently ships a page with that list missing, and the files read is the heavier of the two (it joins share rows on top of the file rows). Those two reads now call the data layer, which also removes a server→server request and its duplicate auth from the render path.

  • Root cause is not confirmed. Staging logs show zero errors from WorkspaceFilesAPI over three days, so this removes the failure mode without proving it was the one firing. What the logs do show on the reported workspace is duplicate requests 13–42ms apart, consistent with the hydrated entry not being used — but those logs don't distinguish server-originated from browser-originated requests. The request dropped from the render path and the shape fix below stand on their own.

  • Shape fix (real bug). listWorkspaceFilesWithShares is shared by the route and the prefetch and shapes its result through the route contract's response schema — the technique prefetchWorkspaceSidebar already documents. listWorkspaceFiles returns contentUpdatedAt, which workspaceFileRecordSchema neither declares nor passes through, so the prefetch was caching a field a client fetch never has and that vanished on the next refetch.

  • Authorization. The reads carry none of their own now that they bypass the route, so the prefetch proves the viewer first. It reuses the layout's cache()d getWorkspaceHostContextForViewer rather than re-deriving the permission, so the gate costs no additional queries. No access → nothing cached → the client fetch reaches the route for the real 403.

Invitations. GET /api/invitations computed join previews in a serial loop, and each preview issues up to three queries of its own — all on the critical path of the workspace switcher opening. Bounded with mapWithConcurrency. Same fetch, same trigger, same payload; the mapper was already total, which is what that helper requires.

Deliberately not included: no conversion of the home/tables/knowledge/chrome prefetches, no lib/table refactor, no invitation prefetching. Those were explored and dropped — the tables conversion is blocked by check:tool-registry-boundary (lib/table/service transitively imports the executor), and prefetching invitations would have put these queries back on every workspace page render.

Type of Change

  • Bug fix

Testing

Tested manually.

  • Three new tests, each verified to fail without its fix: the concurrency bound (set limit to 1 → red), the contract strip (remove the parse → red), and the prefetch auth gate (remove the guard → red).
  • Deleted a test I had written that couldn't fail — it pinned prefetchQuery's swallow behaviour rather than anything in this diff, and passed against origin/staging unchanged.
  • Updated the existing prefetch.test.ts rather than letting it rot, including its graceful-failure case, which was asserting against a fetcher the Files prefetch no longer uses and would have passed vacuously.
  • bun run check:audits, type-check, lint all pass. Pre-existing and unrelated: check:realtime-prune, check:native-typecheck and check:desktop-bridge fail in a worktree with no local node_modules — verified identical on clean origin/staging.

Not browser-verified. Worth one hard refresh of /files with the network tab open before merge: if /api/workspaces/<id>/files still fires as an XHR after load, the hydration issue is something other than what this fixes.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

…itation previews

Two unrelated load-time fixes.

On a hard refresh of /files the folders painted first and the files a beat
later. Both are prefetched and hydrated together, so the files entry was not
reaching the client. Each read went to its own route over an internal HTTP
request; prefetchQuery swallows a rejection and shouldDehydrateQuery drops the
errored entry, so a failure there silently shipped a page with that list
missing, and the files read is the heavier of the two. Those two reads now call
the data layer. Note the staging logs show no errors from that route, so this
removes the failure mode without proving it was the one firing — the request it
drops from the render path, and the shape fix below, stand on their own.

listWorkspaceFilesWithShares is shared by the route and the prefetch and shapes
its result through the route contract's response schema. listWorkspaceFiles
returns contentUpdatedAt, which the schema neither declares nor passes through,
so the prefetch was caching a field a client fetch never has and that vanished
on the next refetch.

The reads carry no authorization of their own now that they bypass the route,
so the prefetch proves the viewer first. It reuses the layout's cached
host-context lookup rather than re-deriving the permission, so the gate costs no
extra queries.

Separately, GET /api/invitations computed join previews in a serial loop and
each preview issues up to three queries of its own, putting all of them on the
critical path of the workspace switcher opening. Bounded with
mapWithConcurrency; the mapper was already total, which is what that helper
requires.
@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 8, 2026 5:53pm

Request Review

@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Prefetch bypasses route auth and relies on host-context gating; incorrect gating could leak file metadata. Invitation concurrency increases parallel DB load but is capped at four.

Overview
Improves Files first paint and invitations switcher latency by changing how server data is loaded, not by new UI.

Files browser: Server prefetch for files and folders no longer hits internal /api routes. It calls listWorkspaceFilesWithShares and listWorkspaceFileFolders directly, with getWorkspaceHostContextForViewer gating access before any read. The files list route uses the same listWorkspaceFilesWithShares helper so route and prefetch share one cached shape. That helper parses through the route contract so fields like contentUpdatedAt are stripped—avoiding hydrated cache entries that diverge from client requestJson and trigger a refetch.

Invitations: GET /api/invitations builds join previews with mapWithConcurrency (limit 4) instead of a serial loop, while still degrading per-row preview failures to null inside the mapper so invitations are not dropped.

Tests cover preview ordering/degradation/concurrency, contract stripping, prefetch auth gate, and updated prefetch suite.

Reviewed by Cursor Bugbot for commit c6b1ba3. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR moves Files-page server prefetching from internal HTTP routes to authorized data-layer reads, normalizes file results through the route contract, and bounds invitation-preview concurrency.

  • Adds a shared file-and-public-share query used by both the API route and server prefetch.
  • Gates direct file and folder prefetches with the workspace viewer’s effective access.
  • Computes invitation join previews with at most four concurrent mapper calls while preserving order and per-row failure handling.
  • Adds focused tests for authorization gating, response shaping, ordering, graceful degradation, and concurrency.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete correctness or security regressions identified.

The direct prefetch uses the same effective workspace-access semantics as the previous routes, file results are normalized through the shared response contract, and the bounded invitation mapper preserves ordering and per-row failure handling.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/files/prefetch.ts Replaces internal route fetches with workspace-authorized data-layer prefetches while retaining the existing query keys and stale times.
apps/sim/lib/workspace-files/queries.ts Centralizes file/share joining and parses results through the files response contract to keep route and hydrated-cache shapes aligned.
apps/sim/app/api/workspaces/[id]/files/route.ts Reuses the shared file/share query after the existing route-level membership check.
apps/sim/app/api/invitations/route.ts Replaces serial preview generation with an order-preserving concurrency limit while retaining per-invitation error degradation.
apps/sim/app/workspace/[workspaceId]/files/page.tsx Supplies the authenticated viewer identity required for the direct data-layer prefetch authorization gate.

Sequence Diagram

sequenceDiagram
  participant Browser
  participant Page as Files Server Page
  participant Auth as Session/Workspace Access
  participant Data as Files/Folders Data Layer
  participant Cache as React Query Cache
  Browser->>Page: Hard refresh /workspace/:id/files
  Page->>Auth: Resolve session and viewer access
  alt Viewer has workspace access
    Auth-->>Page: Authorized host context
    par Prefetch files
      Page->>Data: listWorkspaceFilesWithShares(active)
      Data-->>Page: Contract-shaped files and shares
    and Prefetch folders
      Page->>Data: listWorkspaceFileFolders(active)
      Data-->>Page: Active folders
    end
    Page->>Cache: Prefetch and dehydrate query entries
    Cache-->>Browser: Hydrated initial lists
  else Viewer lacks access
    Auth-->>Page: No host context
    Page-->>Browser: Render without protected cached data
  end
Loading

Reviews (1): Last reviewed commit: "fix(files): read the Files prefetch from..." | Re-trigger Greptile

@waleedlatif1
waleedlatif1 merged commit 8a224be into staging Aug 8, 2026
30 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/files-prefetch-and-invitation-previews branch August 8, 2026 18:10
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