Skip to content

perf: strip unread fields from the shared plugin payload (metric: total byte weight / FCP) - #6

Open
phyceClaw wants to merge 1 commit into
phyce:mainfrom
phyceClaw:perf-trim-shared-plugin-payload
Open

perf: strip unread fields from the shared plugin payload (metric: total byte weight / FCP)#6
phyceClaw wants to merge 1 commit into
phyce:mainfrom
phyceClaw:perf-trim-shared-plugin-payload

Conversation

@phyceClaw

Copy link
Copy Markdown

Metric targeted: Total byte weight → FCP

plugins is a shared Inertia prop (HandleInertiaRequests::share()), so the full 2048-plugin list is embedded in the HTML of every page — not just the homepage. Measured against production:

Page Inertia JSON of which plugins Share
/naturalspeech 925 KB 907 KB 98%
/developers/growing 938 KB 907 KB 97%
/top 969 KB 907 KB 94%
/developers 1,095 KB 907 KB 83%

A plugin detail page currently downloads and parses ~907 KB of plugin records it never renders.

What this changes

Only two things read that shared list: the homepage table (Index.vue) and the header search (scoreSearchResult in formatting.ts). Between them they never touch three fields:

Field Cost across 2048 records
git_repo 133 KB
created_on 80 KB
support 62 KB
total ~272 KB (29.6%)

getClientPlugins() strips them at the point the list is handed to the client.

Why they can't just be removed from the API client

All three are used — just not from this list:

  • PluginDetail.vue reads git_repo, support and created_on from its own single-plugin prop (getPlugin()).
  • TopAbsolute.vue / TopRelative.vue read created_on from entry.plugin (getTopAbsolute()/getTopRelative()).

Those come from different endpoints and are deliberately untouched. GenerateSitemap consumes the same array but only reads name and updated_on, both retained.

Tests

Added tests/Feature/ClientPluginPayloadTest.php, which fakes the API and asserts the shared prop drops exactly those three keys and keeps the ten the frontend reads. Verified it is not vacuous — reverting the controller to getPlugins() makes it fail.

Note: the tests are class-based PHPUnit to match the existing siblings. tests/Pest.php is currently empty, so Pest is not bound to Tests\TestCase and Pest-style it() tests fail with Call to undefined method ::get(). Worth fixing separately.

vendor/bin/pint --dirty passes; full suite green (4 passed).

Deliberately not included

The remaining ~635 KB is description + tags + the table columns, which the search genuinely needs. Getting rid of that means either an Inertia deferred prop or moving search server-side — a behavioural change, so it belongs in its own PR.

The TypeScript Plugin interface is intentionally left as the full shape: narrowing it would break PluginDetail/TopAbsolute, which legitimately read the wider record. Splitting the type is a follow-up.

⚠️ Byte savings are computed from the live payload; the FCP effect is projected, not measured — this VPS is build-only.

🤖 Generated with Claude Code

Metric: total byte weight / FCP (~272 KB uncompressed off every page)

`plugins` is a shared Inertia prop, so the full 2048-plugin list is
embedded in the HTML of every page, not just the homepage. Measured on
production, it dominates the payload everywhere:

  /naturalspeech        925 KB JSON, 907 KB plugins  (98%)
  /developers/growing   938 KB JSON, 907 KB plugins  (97%)
  /top                  969 KB JSON, 907 KB plugins  (94%)
  /developers         1,095 KB JSON, 907 KB plugins  (83%)

Only two consumers read that list: the homepage table and the header
search box. Between them they never touch `git_repo`, `support` or
`created_on`, which cost 133 KB, 62 KB and 80 KB respectively - 29.6% of
the list.

Those three fields are read elsewhere, which is why they cannot simply
be dropped from the API client: PluginDetail reads all three off its own
single-plugin prop, and TopAbsolute/TopRelative read `created_on` off
`entry.plugin`. Both come from different endpoints and are untouched
here. The trim is applied only where the list is handed to the client.

GenerateSitemap consumes the same array but only reads `name` and
`updated_on`, both retained.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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