JustFlows

Документация пока доступна только на английском языке. Остальная часть сайта соответствует вашему языку.

Hooks

Typed actions, gates, and filters — the complete core hook list and how to register them.

10 минута чтения

Hooks are how you change what Justflows does without changing Justflows. Write the plugin in plugins/<name>/ (copy plugins/hello-world) and register hooks in activate(). Names autocomplete from @justflows/sdk.

I want to…Use a…Names look like
React after something happenedactioncontent.published
Stop something before it commitsgatecontent.beforeCreate
Change a value on the way throughfiltercontent.render

Actions

Observe only. Cannot cancel. If your handler throws, Justflows logs it against your plugin and continues — a broken analytics plugin must not block publishing. Treat payloads as read-only. Async handlers are awaited in order.

ActionWhen
app.starting / app.started / app.stoppingProcess lifecycle
content.created / updated / deleted / published / unpublishedContent
media.uploaded / media.deletedMedia
user.created / updated / deletedUsers (needs users:read)
auth.login / logout / loginFailedAuth (needs auth:hook)
plugin.installed / activated / deactivated / uninstalledPlugins
theme.installed / theme.activatedThemes
request.before / request.afterHTTP
site.underConstruction.viewedUnpublished site hit
cache.revalidatedAfter selective cache revalidate
webhook.deliveryAfter each outgoing webhook attempt (status, response, error)
staticExport.completed / staticExport.deployA static / edge export run finished; deploy fires right after so a plugin can push the directory to a CDN

Gates

Run before commit. Call event.cancel("Human-readable reason") to abort. Fail closed: if your gate throws, the operation is aborted and attributed to your plugin. Priority order; first cancellation wins.

  • content.beforeCreate / beforeUpdate / beforeDelete / beforePublish
  • media.beforeUpload / media.beforeDelete
ts
ctx.hooks.gate("media.beforeUpload", (event) => {
  if (event.sizeBytes > 10_000_000) {
    event.cancel("Files must be under 10 MB.");
  }
});

Filters

You must return a value. On core hooks TypeScript enforces it. If a custom filter returns undefined, the previous value is kept and a warning is logged. Throws skip your filter and keep the last good value.

FilterTransforms
content.input / content.outputContent records in/out
content.renderPublic HTML string (may be async)
media.metadataMetadata object
navigation.itemsMenu tree (runs after the host resolves a menu, visibility rules applied)
menu.design.presetsOne-click menu layout presets a plugin/theme ships (static data; ids "<pluginId>:<slug>")
menu.visibility.evaluateAnswer an item-level custom visibility condition your plugin owns (seeded false, fail-closed)
http.responseHeadersHeader map (must be sync)
html.headExtra <head> HTML (must be sync)
theme.cssPlugin CSS appended to cached /theme.css (may be async)
seo.sitemapPathsArray of sitemap path strings
site.underConstruction.renderUnder-construction HTML (must be sync)
comments.renderRendered public comments block HTML (may be async)
header.templates / header.resolve / header.configContribute named header designs, own a page's header per request, or adjust the resolved HeaderConfig before render
webhook.eventTypesArray of deliverable webhook event names
webhook.payloadWebhook JSON body before it is signed and sent
staticExport.routes / staticExport.assetsSeed paths crawled by the exporter, and same-origin asset URLs the scanner cannot discover
staticExport.formActionOverride the <form action> written into exported HTML for a dynamic endpoint

SYNC_FILTERS: http.responseHeaders, html.head, site.underConstruction.render — handlers must not be async. content.blocks, content.render, and theme.css may be async.

Options and ownership

  • priority — lower runs earlier (default 100).
  • once — auto-dispose after first dispatch.
  • id — stable label in diagnostics.
  • Plugins may emit only hooks under their own manifest id (acme.seo.scoreCalculated).
  • Declaration merging on ActionEventMap / GateEventMap / FilterValueMap types your own names.

Header and comment hooks

header.templates contributes named headers that appear in the per-page picker, build()-rendered at request time. header.resolve lets a plugin own a page's header per request; header.config adjusts the resolved header before render. comments.render restyles or replaces the rendered comments block. New SDK types: HeaderConfig, HeaderTemplate, HeaderBuildContext, HeaderResolveContext, CommentsBlockRenderContext, PublicComment. Outgoing webhooks add two filters — webhook.eventTypes to register event names and webhook.payload to reshape a body — plus the webhook.delivery action after every attempt; see Webhooks. Access-policy changes emit access-change hooks; a plugin registers a runtime capability with ctx.capabilities.register() and declares cookies with ctx.cookies.declare() (reading the resolved site registry with ctx.cookies.list()). The synchronous analytics.head filter lets Cookie consent defer third-party tags until consent is granted. For menus, menu.design.presets contributes one-click layout presets to the designer's design panel and menu.visibility.evaluate resolves a plugin-defined item condition — an unrecognized or deactivated condition id hides the item. See docs/HOOKS.md → Contributing a menu design preset in the Community Edition repository.

Note

Longer narrative and cleanup rules: docs/HOOKS.md in the Community Edition repository.