diff --git a/README.md b/README.md index 282a153..30a8348 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,25 @@ Both demo Blueprints explicitly request PHP `intl` support because editor-time C Developer-facing CI/runtime coverage details are listed in the development section below. +## Footprint & Performance + +Borges is a static-output block: formatted bibliography HTML, JSON-LD, and COinS are baked into post content at save time, so **published pages add zero database queries and zero server-side formatting** — the citeproc engine and metadata lookups run only while you edit. All figures below are hand-verified; re-derivation commands live in [`docs/current-metrics.md`](docs/current-metrics.md). + +| Metric | Value | +|---|---| +| First-party PHP (main plugin file) | ~1,880 LOC (single file) | +| JS source (`src/`) | ~8,850 LOC | +| Frontend runtime shipped to visitors | `view.js` ~1.4 KB + `style-index.css` ~2.9 KB, enqueued only when the block is present | +| Installed footprint | ~2.1 MB (`vendor/` ~1.0 MB incl. citeproc-php and curated CSL styles, translations 724 KB, assets 328 KB) | +| Distributed ZIP (compressed) | ~0.9–1 MB | +| **Added DB queries per page** | **0** — regardless of block or citation count | +| Autoloaded options / registered settings / cron / custom tables / custom post types | none | +| `render_callback` on the frontend | none (static `save()` only) | + +The only per-visitor cost is the small `view.js`/`style-index.css` pair, loaded solely on pages that contain a bibliography. Deactivating the plugin leaves the rendered bibliographies intact as static HTML. + +Editor-time PMID and formatting results are cached in the object cache and in short-lived, non-autoloaded `_transient_bbb_*` transients — written only while editing, never on a visitor request. (DOI imports are deduped in a browser-session cache, not stored server-side.) + ## Recent Release Highlights - **1.3.4** — Refreshes the translation template plus 19 seed PO/MO locale pairs, adds CI validation for i18n artifacts, clarifies the bundled seed versus official language-pack policy, and archives historical planning notes out of active docs. diff --git a/docs/current-metrics.md b/docs/current-metrics.md new file mode 100644 index 0000000..27cbef0 --- /dev/null +++ b/docs/current-metrics.md @@ -0,0 +1,93 @@ +# Current metrics + +Hand-verified size, footprint, and runtime-overhead metrics for Borges Bibliography Builder. +Each number carries the exact command used to re-derive it, so the figures can be re-checked +on demand rather than trusted on faith. Re-run the relevant command and update the number **in +the same commit** whenever the underlying quantity changes. + +Last verified: **2026-07-05** against `main` (commit `49f623b`). + +## Lines of code + +| Metric | Value | Re-derivation command | +|---|---|---| +| Main plugin file (`bibliography-builder.php`) | **1,881** | `wc -l bibliography-builder.php` | +| All first-party PHP (excl. vendor, tests, scripts, packages, output, node_modules, generated `build/`) | **1,979** | `find . -name '*.php' -not -path './vendor/*' -not -path './node_modules/*' -not -path './tests/*' -not -path './packages/*' -not -path './scripts/*' -not -path './output/*' -not -path './build/*' -print0 \| xargs -0 wc -l \| tail -1` | +| JS source (`src/`, excl. `*.test.js`) | **8,851** | `find ./src -name '*.js' -not -name '*.test.js' -print0 \| xargs -0 wc -l \| tail -1` | +| Shipped frontend runtime (`build/view.js`, minified) | **1,449 bytes** | `npm run build` then `wc -c < build/view.js` | + +The only PHP that executes at runtime on a visitor request path is `bibliography-builder.php` +(REST registration + block registration); the CSL formatting engine under `vendor/` runs +**only** for editor-time REST calls. `scripts/*.php` are dev tooling and are not packaged. + +## Storage footprint (installed) + +Measured from a full release build (`npm run package:release`), which copies the shipped +files and installs runtime Composer dependencies with `--no-dev`, then prunes tests/docs/images +from `vendor/`. + +**Prerequisite:** `build/` is gitignored (produced by `npm run build`), and +`scripts/package-release.sh` copies it, so run **`npm run build`** first on a clean checkout — +otherwise `package:release` (and the `du -sh build` row below) fails with `cannot stat build`. + +| Component | Size | Re-derivation command | +|---|---|---| +| `vendor/` — citeproc-php engine + `seboettg/collection` + `myclabs/php-enum` + curated `citation-style-language/styles` & `/locales`, pruned | **~1.0 MB** | `du -sh output/release/borges-bibliography-builder/vendor` (after `npm run package:release`) | +| `languages/` — seed PO/MO/JSON translations | **724 KB** | `du -sh languages` | +| `build/` — editor + frontend assets | **328 KB** | `du -sh build` | +| PHP + `block.json` + `readme.txt` + `LICENSE` + `THIRD-PARTY-NOTICES.txt` | **~100 KB** | — | +| **Total installed** | **~2.1 MB** | `du -sh output/release/borges-bibliography-builder` (after `npm run package:release`) | +| Distributed ZIP (compressed) | **~0.9–1 MB** | `du -h output/release/borges-bibliography-builder.zip` | + +The source tree's `packages/` directory (60 KB) is **not** a separate shipped component: the +release script (`scripts/package-release.sh`) Composer-installs those path packages into +`vendor/citation-style-language/*` and then deletes the staged `packages/` (`rm -rf`). The +curated CSL styles are therefore counted inside the `vendor/` figure above. + +Notes: +- `vendor/seboettg/citeproc-php` is **3.2 MB** unpruned and **~536 KB** after the release + script strips tests/docs. The pruning is what keeps the installed footprint near 2 MB. +- The bundled `citation-style-language/styles` is a **curated subset** (the nine styles the + plugin ships), not the full upstream repository (~40 MB). This is a deliberate footprint + control, not an accident of packaging. + +## Runtime / query overhead + +The block uses **static `save()` output with no `render_callback`** — formatted bibliography +HTML, JSON-LD, and COinS are serialized into `post_content` at edit time. The visitor request +path therefore adds **zero** database queries and **zero** citeproc/PHP formatting work. + +| Overhead on the frontend (per published page) | Value | Re-derivation | +|---|---|---| +| Additional database queries | **0** | Static save; nothing on the block runs on the frontend — see audit below | +| REST calls | **0** | `POST /format` and `GET /pmid/{pmid}` fire only in the editor | +| `render_callback` invocations | **0** | Block registers no server render | +| Autoloaded options / registered settings | **0** | No `add_option`/`update_option`/`register_setting` | +| Cron events | **0** | No `wp_schedule_event` | +| Custom post types / custom tables | **0** | No `register_post_type` / `dbDelta` | +| Enqueued frontend assets (only when block present) | `view.js` ~1.4 KB + `style-index.css` ~2.9 KB | `wc -c build/view.js build/style-index.css` | + +Persistence/hook audit — no persistent settings, options, cron, CPTs, or custom tables +(expected output: **NONE**): + +``` +grep -rEl "add_option|update_option|register_setting|register_post_type|dbDelta|wp_schedule_event" \ + --include='*.php' . | grep -vE 'vendor|tests|node_modules' +``` + +**One caveat on `wp_options`.** On every store, the plugin's editor-time cache +(`bibliography_builder_cache_set()`) calls **both** `wp_cache_set()` and `set_transient()` — +they are two independent `if ( function_exists() )` guards, not an object-cache-else-transient +fallback. With a persistent object cache drop-in active, `set_transient()` also routes to the +object cache and **no** option row is written; without one, it materializes +**non-autoloaded, expiring** `_transient_bbb_*` rows in `wp_options`. Either way the write +happens **only during editor REST calls** (`/format`, `/pmid`) as short-lived caches of +upstream lookups — never on a visitor request, and never added to the autoload set. So there +are no long-lived settings and nothing on the autoload path, but "zero rows ever touch +`wp_options`" would be inaccurate on a site without an external object cache. (DOI imports are +deduped separately in a browser-session JS `Map` in `src/lib/parser.js` and never reach this +PHP cache.) + +The "average queries per page" figure is a flat **0 additional queries**, independent of the +number of bibliography blocks or citations on the page — all expensive resolution (Crossref +DOI, NCBI/PMC PMID) and CSL formatting happens at edit time and never touches a visitor. diff --git a/readme.txt b/readme.txt index b165682..d1ff88f 100644 --- a/readme.txt +++ b/readme.txt @@ -102,6 +102,21 @@ PHP 7.4+ and WordPress 6.4+. Borges Bibliography Builder is tested up to WordPre 8. Export the whole bibliography from the sidebar: copy as plain text, or download CSL-JSON, BibTeX, BibLaTeX, or RIS. 9. Readers can expand the per-entry Cite / Export panel on the published page to copy a citation or download it as RIS, CSL-JSON, BibTeX, or BibLaTeX. +== Performance & Footprint == + +Borges is a static-output block. Formatted bibliography HTML, JSON-LD, and COinS are baked into your post content when you save, so the citeproc formatting engine and the DOI/PubMed lookups run **only in the editor** — never on the front end. + +What this means for a published page: + +* **Zero added database queries** — no matter how many bibliographies or citations the page contains. Query load does not grow with your content. +* **No `render_callback`, no REST calls, no shortcodes** on the front end. Output is static HTML from the block's `save()`. +* **No long-lived settings** — the plugin registers no settings or autoloaded options, no custom tables, no custom post types, and no cron events. Editor-time PMID and formatting results are cached in the object cache and in short-lived, non-autoloaded transients, written only while editing. DOI imports are deduped in a browser-session cache, not stored server-side. +* **Tiny front-end payload** — only a small view script (~1.4 KB) and stylesheet (~2.9 KB) load, and only on pages that actually contain a bibliography. + +Installed footprint is roughly **2.1 MB** (the bundled citeproc-php formatting engine accounts for about half; the rest is translations, editor/front-end assets, and a curated set of citation styles). The downloadable ZIP is about 0.9–1 MB compressed. + +Because output is static, your bibliographies remain intact as plain HTML even if the plugin is deactivated. + == Known Limitations == **OSCOLA grouped bibliography** — OSCOLA convention requires the bibliography to be divided into source-type groups (cases, legislation, books, articles, online sources). Borges currently renders a single alphabetized list regardless of style. A dismissible notice in the editor explains this when OSCOLA is selected. Grouped-bibliography support is planned for a future release.