refactor(resources): one canonical view per resource, mounted on source/grants/host - #6279
refactor(resources): one canonical view per resource, mounted on source/grants/host#6279waleedlatif1 wants to merge 3 commits into
Conversation
…ce/grants/host
Extracts the file and table views out of their route folders into canonical
units under `apps/sim/components/resources/**`, and gives every consumer one way
to mount them. Behavior-neutral: no page, no API route, and no migration is
added or removed, and every default is preserved.
The three axes (`apps/sim/resources/**`, pure TypeScript so a Server Component
can build one during SSR):
- `source` — where the data comes from and by what address, `WorkspaceSource<K> |
ShareSource<K>` discriminated on `via`. Replaces `workspaceId`, `token`,
`contentSource`, `isPublic`. `ShareSource` declares `workspaceId?: never`, so a
share token can no longer be laundered through a workspace-shaped slot.
- `grants` — what this viewer may do: `{ write, run }`. Replaces `canEdit`,
`canRun`, `canAdmin`, `disableEdit/Insert/Delete`.
- `host` — who owns the URL, the router, the document frame. Replaces
`embedded`; `hostOwnsUrl(host)` is the single home of the "embedded views do
not write nuqs keys" rule.
`file` is the only kind with a share seed, because `/f/[token]` is the only
public route that serves one. Every other kind is `never` and cannot be
addressed by a token at all — a compile-time fact rather than a runtime check.
Also in this PR:
- Public share auth (password / email-OTP / SSO) moves out of `app/f/[token]`
into `components/public-share/**`, behind one page-side gate. Security
hardening on the way: timing-safe OTP comparison, a per-IP bucket on OTP
verify separate from the send path, and share tokens removed from logs.
- Log status chrome (`StatusBadge`, `STATUS_CONFIG`, `getDisplayStatus`) moves
to `components/execution-status/`, shared instead of per-surface.
- `AnchoredContextMenu` and `ActionRow` move to `components/`, which closed a
real hole: `file-view` is mounted by `app/f/[token]` for anonymous visitors,
and with no `sideEffects: false` its import of the workspace barrel pulled the
whole authenticated tree into the public chunk.
- emcn: `ChipPasswordInput`, a shared `ChipChevronDown`, and byte-range /
untitled-title utilities behind the file preview routes.
- Deployed-chat header now renders the shared `Navbar` in `logoOnly` mode, so a
public chat wears the same wordmark, geometry, name and "Shared by" credit as
the shared file page. This drops the GitHub star chip from that header.
- `ActionRow` used `var(--divider)`, which is not a defined token, so its
dividers fell back to `currentColor`. Now `var(--border)`.
Nothing here advertises a capability it cannot deliver: the `form` trigger type
that arrived with this work drives nothing on its own, so it is not registered in
the Start block's trigger list, the Logs filter, the execute contract, or the
executor's streaming types. It lands with the surface that produces it.
Enforced by `bun run check:resources` (added to CI), which ratchets counters for
wrappers, imports past a unit barrel, cross-tree imports, unsanctioned props,
token-as-workspaceId, and context leaks. The cross-tree counter is re-based to
the current tree: every offender it counts is inherited, and this change strictly
reduces the number.
Verification: apps/sim + 22 packages typecheck, biome clean, 18598 tests pass,
and all 15 repo gates pass including both strict variants.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryCursor Bugbot is generating a summary for commit 001cbb4. Configure here. |
Greptile SummaryThe PR consolidates file and table resource rendering around canonical views parameterized by source, grants, and host, while reorganizing shared UI and public-share infrastructure.
Confidence Score: 5/5The PR appears safe to merge because no blocking failure remains within the eligible follow-up-review scope. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/resources/source.ts | Introduces discriminated workspace/share sources, preventing share sources from carrying workspace identifiers or producing workspace links. |
| apps/sim/components/resources/file-view/file-view.tsx | Consolidates workspace, panel, streaming, and public file rendering into one canonical view driven by source, grants, and host. |
| apps/sim/app/f/[token]/public-file-view.tsx | Mounts the canonical file view with a token-based share source, read-only grants, and public host behavior. |
| apps/sim/app/api/files/public/[token]/content/route.ts | Refactors public content serving while retaining active-share resolution, deployment authentication, rate limiting, and ranged media responses before storage access. |
| apps/sim/components/resources/table-view/index.ts | Establishes shared table-view exports and data contracts used by the workspace table grid. |
| apps/sim/hooks/queries/tables.ts | Updates table query and mutation integration while preserving typed contracts and authoritative cache refresh behavior. |
| scripts/check-resource-views.ts | Adds static enforcement for canonical mounts, import boundaries, axis purity, and prohibited route or permission context access. |
| .github/workflows/test-build.yml | Adds the resource boundary audit to CI and removes the explicit ripgrep installation step. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Workspace[Workspace page] --> WS[Workspace source]
Panel[Embedded panel] --> WS
Public[Public share page] --> SS[Share source]
Permissions[Workspace permissions] --> WG[Workspace grants]
SharePolicy[Share policy] --> SG[Read-only share grants]
WS --> View[Canonical resource view]
SS --> View
WG --> View
SG --> View
PageHost[page host: owns URL] --> View
PanelHost[panel host: no URL ownership] --> View
PublicHost[public host: no workspace links] --> View
View --> File[File content and collaboration]
View --> Table[Table rendering and editing]
Reviews (3): Last reviewed commit: "fix(file-view): correct the data-table s..." | Re-trigger Greptile
Navbar's actions slot is not gated by hideBrand, so a whitelabeled public chat or shared file rendered a Share menu whose drafted post advertised Sim. Compose the mention from brand.name when brand.isWhitelabeled, matching the rule the email footer already applies.
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c013365. Configure here.
data-table.tsx moved one directory deeper during the migration but kept its './document-table.css' import. The stylesheet is shared at the components/ level — both rich-markdown-editor importers already use '../document-table.css' — so this was the lone stale path. TypeScript does not resolve bare CSS imports, so only the production build caught it.
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 4da3df5. Configure here.
Summary
source(workspace vs share),grants(write/run), andhost(page/panel/public) — defined as pure TypeScript inapps/sim/resources/**shareSource.hrefForalways returnsnull, and a kind with no public route is typedneverinResourceSeedMap, so an anonymous surface cannot construct a workspace URL or address a resource the share does not granthostOwnsUrl(host)— the single place the "only a page owns the address bar" rule is spelled — so embedded views stop writing nuqs keys into the parent's URLscripts/check-resource-views.ts, a ratcheted gate covering wrapper mounts, shadow-namedEmbedded*components, imports past a barrel, cross-tree imports, unsanctioned props, and'use client'leaking into the axis modulesBehavior-neutral for users, with three deliberate exceptions:
ActionRow's divider referencedvar(--divider), which is not defined anywhere — it silently fell back tocurrentColor. Nowvar(--border).'form'trigger type that was advertised in six registries with nothing producing it. It would have rendered a dead "Form" chip in the Logs filter, a dead trigger option in the workflow editor, and advertised aformtrigger to copilot block metadata.lucide-reactimports to@sim/emcn/icons.lucide-reactis no longer inpackage.json, so two of these files would have failed the build.Type of Change
Testing
Typecheck clean, all 15 repo gates pass, biome clean across every changed file, full suite green (18,626 tests).
Not visually verified. Worth a look before merge — the knowledge base page is the highest risk, since its header, toolbar, and table were restructured under
ResourceRoot.Checklist