Skip to content

feat(plugin-host): versioned updates + frontend extensions + custom block render + apply_filters_batch - #490

Merged
tayebmokni merged 4 commits into
mainfrom
feat/plugin-host-gaps
May 26, 2026
Merged

feat(plugin-host): versioned updates + frontend extensions + custom block render + apply_filters_batch#490
tayebmokni merged 4 commits into
mainfrom
feat/plugin-host-gaps

Conversation

@tayebmokni

Copy link
Copy Markdown
Contributor

Closes #63, #206, #222, #263.

tib0o0o added 4 commits May 26, 2026 23:49
Adds Bus.ApplyBatch that dispatches a whole slice through a filter
chain in one call, instead of N ApplyFilters invocations. Plugins opt
in via the manifest's flags.apply_filters_batch boolean; the bus
exposes RegisterBatchFilter for the wiring layer to call when the
flag is set. Legacy per-item handlers continue to work inside a
batched chain (the bus loops them transparently).

Microbenchmark on M3 Pro, 100 items per dispatch:

  BenchmarkApplyFilters_PerItem    107333 ns/op  100801 B/op  600 allocs/op
  BenchmarkApplyBatch_BatchAware      770 ns/op    4592 B/op    8 allocs/op
  BenchmarkApplyBatch_LegacyHandler   828 ns/op    4592 B/op    8 allocs/op

Closes #263.

Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
Extends the render walker so block types of the form
plugin/<slug>/<handler> dispatch through the hook bus instead of the
local registry. The walker recurses into InnerBlocks first (so the
plugin receives already-rendered children HTML), wraps the block's
attributes + inner + context into PluginBlockRequest, and routes it
to a PluginBlockDispatcher.

HookBusDispatcher implements the dispatcher by firing
ApplyFilters("block.render:<slug>/<handler>", payload) on the host
bus. The plugin's WASM handler subscribes to that key and returns
the rendered HTML; the dispatcher accepts template.HTML, string,
[]byte, or json.RawMessage shapes (json-encoded strings are
unwrapped). Errors degrade to the same render-error placeholder a
local renderer error produces — one bad plugin doesn't take the
whole page down.

Closes #222.

Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
Adds apps/api/internal/plugins/frontend, the host-side handler that
serves plugin web/ bundles as ES modules under
/api/plugins/{slug}/web/{path} and composes a page-level import map
of every active plugin's declared module exports.

Each bundle entry's SHA-256 is precomputed at Register and reused for
SRI (X-SRI header + ImportMapScriptTag integrity hints), ETag, and
long-TTL Cache-Control headers. The composed import map is exposed
via GET /api/plugins/import-map.json with stable sorted-key output
and a short TTL so plugin activation flips are picked up quickly.
ImportMapSnapshot + ImportMapScriptTag help the SSR template embed
the map inline.

Path traversal, oversized files, and import-map collisions are
rejected at Register before any state mutation, so a bad bundle
leaves the registry untouched. Pairs with the TS-side
plugin-frontend-host primitives.

Closes #206.

Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
Adds Manager.Update / Rollback / RunRetentionCleanup on top of the
existing lifecycle Manager. Update stages a new version side-by-side
via Runtime.Load, records it in the new plugin_version_log table
(migration 000039), drains in-flight requests against the previous
version (drainTracker poll, configurable timeout, default 30s),
atomically swaps the active pointer, and marks the previous row
retained with a 24h-by-default retention_end.

Rollback re-promotes the most recent retained version (or a named
one) and runs the same drain + retain cycle on the version it
replaces, so rollbacks remain reversible. RunRetentionCleanup is the
cron entrypoint that purges retained rows past retention_end and any
rows marked retired.

Storage layer:
  * MemoryVersionLog and PostgresVersionLog implement the new
    VersionLog interface.
  * Memory + Postgres Storage backends gain UpdateActiveVersion via
    the optional VersionedStorage extension, used to mirror the
    swap onto the plugins row without disturbing the State CAS.
  * Manifest schema gains flags{apply_filters_batch} (unrelated to
    #63 but pairs with the version-tracking metadata).

Construct a Manager with EnableVersionedUpdates(...) to opt in;
managers built without it retain the legacy behaviour and return an
"unsupported" error from Update / Rollback.

Closes #63.

Signed-off-by: Tayeb Mokni <tayeb.mokni@gmail.com>
@tayebmokni
tayebmokni enabled auto-merge (squash) May 26, 2026 22:02
@github-actions

Copy link
Copy Markdown

Heads up — this PR touches strings that often signal a security disclosure (vulnerab, CVE-, exploit, bypass, or auth bypass).

If this PR fixes or describes a real vulnerability that has not yet been publicly disclosed, please stop and use the private path:

  1. Open a private security advisory, or
  2. Email security@gonext.io with subject [SECURITY] GoNext - <summary>.

See /SECURITY.md for the full disclosure flow and /docs/16-bug-bounty.md for bounty terms.

If this is a false positive (test fixture, doc update, release notes, etc.) please ignore this comment — the check is advisory only and does not block the PR.

Matched files:

  • apps/api/internal/plugins/frontend/handler.go
  • packages/go/plugins/lifecycle/versions_postgres.go

@tayebmokni
tayebmokni merged commit f9b3997 into main May 26, 2026
7 of 11 checks passed
@tayebmokni
tayebmokni deleted the feat/plugin-host-gaps branch May 26, 2026 22:02
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.

Plugin updates: versioned install + atomic swap + drain + rollback retention

2 participants