Skip to content

feat(examples/plugins/seo): worked SEO plugin proving the runtime end-to-end — closes #269#403

Merged
tayebmokni merged 1 commit into
mainfrom
feat/seo-plugin-example
May 19, 2026
Merged

feat(examples/plugins/seo): worked SEO plugin proving the runtime end-to-end — closes #269#403
tayebmokni merged 1 commit into
mainfrom
feat/seo-plugin-example

Conversation

@tayebmokni
Copy link
Copy Markdown
Contributor

Summary

  • examples/plugins/seo/ — a TinyGo-built plugin that ships manifest.json (apiVersion gonext.io/v1), four exported ABI symbols (gn_alloc, gn_free, gn_handle_hook, gn_handle_job), and a build script. Subscribes to the_content (filter, injects schema.org JSON-LD), wp_head (action, emits <title> + <meta> + OpenGraph + Twitter card), and save_post (action, computes SEO score). Owns one background job (seo.recompute-scores).
  • domain.go — pure Go helpers (BuildTitle, BuildDescription, BuildHeadHTML, BuildJSONLD, ComputeSEOScore) split out so the test suite exercises the same code the WASM blob links — no TinyGo required to run go test.
  • dummy_host_test.go — Go-side fake host bus that mirrors the wazero dispatcher path, proving the wire format the plugin speaks matches what packages/go/plugins/abi/hooks/marshal.go produces. Canonical copy at packages/go/plugins/internal/_seo_dummy.go (leading underscore so Go skips it for builds; doc-only).
  • seo_test.go — manifest validates against packages/go/plugins/manifest/schema.json; unit tests for all domain functions; HTML escaping; JSON-LD parses + carries Article required fields; pinned-score table; end-to-end filter run.
  • docs/04-seo-plugin-tutorial.md — a 30-minute "build this plugin from scratch" walkthrough that mirrors the implementation file by file.
  • go.work — adds ./examples/plugins/seo so the example builds alongside the workspace; the example has its own go.mod so TinyGo doesn't drag in the host's Postgres/Redis/Asynq deps.

Capability rationale (the plugin uses every plumbed capability so the install dialog has a real-world cap list to render):

Capability Why it's needed
posts.read Read the post being saved when computing the SEO score.
posts.write Persist score to _seo_score post meta (via host call).
hooks.subscribe Required to register the three hook subscriptions.
jobs.enqueue Required to enqueue seo.recompute-scores.

Test plan

  • go test -race -count=1 ./examples/plugins/seo/... — 14 tests pass (manifest schema, unit tests, JSON-LD validity, score table, end-to-end filter)
  • cd packages/go && go test -race -count=1 ./plugins/lifecycle/... ./plugins/abi/hooks/... — pass (runtime regression check per the brief)
  • cd packages/go && go test -race -count=1 ./plugins/manifest/... — pass
  • bash examples/plugins/seo/build.sh — exits with a helpful "tinygo not on PATH" message when toolchain missing; README documents the install URL
  • Manual: install TinyGo, run ./build.sh, pack into seo.gnplugin via zip -j seo.gnplugin manifest.json seo.wasm, install + activate via the lifecycle Manager (deferred — requires the admin install dialog from feat(plugins/runtime): wazero-backed WASM runtime foundation (#6) #350)

🤖 Generated with Claude Code

@tayebmokni tayebmokni enabled auto-merge (squash) May 19, 2026 08:18
…-to-end — closes #269

## Summary

- `examples/plugins/seo/` — a TinyGo-built plugin that ships
  `manifest.json` (apiVersion gonext.io/v1), four exported ABI symbols
  (`gn_alloc`, `gn_free`, `gn_handle_hook`, `gn_handle_job`), and a
  build script. Subscribes to `the_content` (filter, injects
  schema.org JSON-LD), `wp_head` (action, emits `<title>` + meta +
  OpenGraph + Twitter card), and `save_post` (action, computes SEO
  score). Owns one background job (`seo.recompute-scores`).
- `domain.go` — pure Go helpers (`BuildTitle`, `BuildDescription`,
  `BuildHeadHTML`, `BuildJSONLD`, `ComputeSEOScore`) split out so the
  test suite exercises the same code the WASM blob links — no
  TinyGo required to run `go test`.
- `dummy_host_test.go` — Go-side fake host bus that mirrors the
  wazero dispatcher path, proving the wire format the plugin speaks
  matches what `packages/go/plugins/abi/hooks/marshal.go` produces.
  Canonical copy at `packages/go/plugins/internal/_seo_dummy.go`.
- `seo_test.go` — manifest validates against
  `packages/go/plugins/manifest/schema.json`; unit tests for all
  domain functions; HTML escaping; JSON-LD parses + carries Article
  required fields; pinned-score table; end-to-end filter run.
- `docs/04-seo-plugin-tutorial.md` — a 30-minute "build this plugin
  from scratch" walkthrough.
- `go.work` — adds `./examples/plugins/seo` so the example builds
  alongside the workspace; the example has its own `go.mod` so
  TinyGo doesn't drag in the host's Postgres/Redis/Asynq deps.

The plugin uses every plumbed capability (`posts.read`,
`posts.write`, `hooks.subscribe`, `jobs.enqueue`) so the install
dialog has a real-world cap list to render.

## Test plan

- [x] `cd /tmp/gn-i11 && go test -race -count=1 ./examples/plugins/seo/...` — 14/14 pass
- [x] `cd packages/go && go test -race -count=1 ./plugins/lifecycle/... ./plugins/abi/hooks/...` — pass
- [x] `cd packages/go && go test -race -count=1 ./plugins/manifest/...` — pass
- [x] `cd examples/plugins/seo && bash build.sh` — exits with helpful "tinygo not on PATH" message when toolchain missing; documented in README
- [ ] Manual: install TinyGo, run `./build.sh`, pack into `seo.gnplugin` via the documented zip recipe, install + activate via the lifecycle Manager (deferred — requires admin UI from #350)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Mohamed Tayeb Mokni <tayeb.mokni@gmail.com>
@tayebmokni tayebmokni force-pushed the feat/seo-plugin-example branch from bc77989 to 00f3c05 Compare May 19, 2026 08:26
@tayebmokni tayebmokni merged commit 7abc37d into main May 19, 2026
17 of 19 checks passed
@tayebmokni tayebmokni deleted the feat/seo-plugin-example branch May 19, 2026 09:24
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.

2 participants