feat(plugin-host): versioned updates + frontend extensions + custom block render + apply_filters_batch - #490
Merged
Merged
Conversation
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
enabled auto-merge (squash)
May 26, 2026 22:02
|
Heads up — this PR touches strings that often signal a security disclosure ( If this PR fixes or describes a real vulnerability that has not yet been publicly disclosed, please stop and use the private path:
See 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:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #63, #206, #222, #263.