Files
fofos-admin/manifest.ts
T
pastilhasandClaude Opus 5 db781701e3 measure the site instead of reading a report back
Every performance decision here was made from a Lighthouse run somebody did by
hand in DevTools and typed into a conversation. That loop is slow and it is
lossy: nothing measured after a build, so three real defects sat live for weeks
— a `sizes` that overstated a column by 48px, font preloads that had gone
missing, an LCP image left lazy by a rename.

api/lighthouse.ts runs it: both form factors in about 20 seconds, four
categories, the five metrics, and every failing audit with the resources it
names. A finding that says "Improve image delivery — 32 KiB" is not actionable;
the same finding with the URL and the size served is a one-line fix.

The browser is looked for in three places before anything is downloaded —
CHROME_PATH, chrome-launcher's own search, then the Chrome-family binaries by
name, which is what finds the Brave already on this machine. chrome-launcher
THROWS rather than returning null when it finds nothing, so an unguarded call
never reaches the fallback; that is caught. Only if all three miss does
@puppeteer/browsers fetch one, as a library call rather than a postinstall,
because bun does not run postinstall for an untrusted package and a silent no-op
there would surface as a failure at the first audit.

First result: Total Blocking Time is 0 ms on both form factors, down from 130.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 19:22:33 +01:00

115 lines
5.5 KiB
TypeScript

import type { PluginManifest } from '@@/plugins/manifest';
/**
* Fofos — the fofos website's admin, inside Officer.
*
* ── What it replaces ──
*
* A server-rendered admin on its own subdomain with its own argon2 passwords, its own session cookie
* and its own `/admin/setup` bootstrap. Every part of that duplicated something Officer already does,
* and none of it could be shared: giving somebody access to the machine still meant giving them
* access to the admin, separately, by hand. That is the whole reason this is a plugin.
*
* ── It holds no content ──
*
* fofos owns its own database and its own media store, and this reads and writes them over HTTP. The
* only thing in Officer's database is the connection — a URL and a token.
*
* That is a deliberate property rather than a convenience. **Deleting this plugin cannot lose a
* photograph or a line of the client's copy**, with the "delete its data" checkbox ticked or not,
* because none of it was ever here. The alternative — a `db/schema.ts` carrying fofos's own tables —
* would have put every one of them under an installer that is allowed to drop them.
*
* ── The permission is grantable; the credential is not ──
*
* Reading and editing the site is exactly what a member might be brought in to do. The TOKEN is a
* different matter: it is unrestricted write authority over everything the public sees, so `/_config`
* is `ownerOnly` — the split that field exists for, and the same shape proxy-manager and Jellyfin use
* for the same reason.
*
* ── No `screens` ──
*
* One screen at the mount prefix, labelled `label`: the sections are panels of one workspace, and
* `screens` is for a plugin contributing genuinely separate destinations with their own dock tiles.
*
* They are URLs under it, though — `/fofos-admin/layout/header`, `/fofos-admin/pages/home`. The
* platform hands this plugin the whole subtree and nothing more; which paths exist and what they mean
* is web/routes.ts, here, where it belongs.
*/
export const manifest: PluginManifest = {
publisher: 'officerdev',
version: '2026.08.29.26',
platform: '>=1.0.0',
label: 'Fofos',
summary: "The fofos website's content — pages, galleries, drops and collabs",
/** one website's admin, and useless without that website. */
tags: ['Personal'],
icon: 'PenLine',
color: '#c2410c',
// Nothing to declare. It talks HTTP to fofos and stores one row; there is no host binary anywhere
// on the path, and fofos itself is a separate deployment rather than something this installs.
/*
The rich text editor for the site's copy.
Declared here rather than added to the platform's package.json, which is frozen and shared: these
are installed into `plugins/node_modules/` alongside every other plugin's, so nothing about this
plugin's editor can move a version the platform depends on.
`@tiptap/extension-text-align` was already in the platform's dependencies with nothing importing
it — a leftover from an earlier attempt. It is asked for here too, so this plugin's copy is
resolved by the same rule as the rest rather than by whatever happens to be up the tree.
*/
npmDependencies: {
'@tiptap/react': '^3.15.3',
'@tiptap/starter-kit': '^3.15.3',
'@tiptap/pm': '^3.15.3',
'@tiptap/extension-text-align': '^3.15.3',
'@tiptap/extension-link': '^3.15.3',
/*
Lighthouse, so the site can be MEASURED from the panel that builds it.
Every performance decision on this site so far — the `sizes` a column declares, which image is
preloaded, whether the lightbox is parsed at load — was made by reading a report somebody ran by
hand in DevTools and typed back. That is slow and it is lossy: three real defects (a `sizes` that
overstated a column, font preloads that had gone missing, an LCP marked lazy) sat live for weeks
because nothing measured after a build.
A big tree, and worth saying so given bunfig.toml: ~100 transitive packages. It brings its own
`chrome-launcher` and `puppeteer-core`, so neither is asked for here.
`@sentry/node` arrives with it. Lighthouse's error reporting is opt-in — off unless
`--enable-error-reporting` — and this never passes it. See api/lighthouse.ts.
*/
lighthouse: '^13.4.1',
/*
Fetches a Chrome BINARY, at runtime rather than in a postinstall.
Lighthouse needs a browser and this machine has none: Ubuntu 24.04's apt `chromium` is the snap
stub the iris manifest warns about, and there is no Chrome repo. Puppeteer's own download runs in
a postinstall, which bun does not execute for an untrusted package — it would silently install
nothing and fail at the first audit.
So the download is a library call the plugin makes, once, when it first needs a browser. Explicit,
observable, and it narrates — see api/lighthouse.ts.
*/
'@puppeteer/browsers': '^3.2.2',
},
permissions: [
{
key: 'fofos-admin',
label: 'Fofos',
description: 'Edit the fofos website — pages, galleries, drops, collabs and the messages people send',
// Where the fofos URL and token are set, and the probe that checks them. Prefix-matched, so
// `/_config/test` is covered too — deliberately, because that request carries the token in its
// BODY. A member who could call it would have an oracle pointed at a credential that can
// rewrite the whole public site.
ownerOnly: ['/_config'],
},
],
};