pluginsdk/abi/proto/v1/render.proto

280 lines
9.2 KiB
Protocol Buffer

// render.proto — block/template render payloads and the explicit
// render-context envelope (WO-WZ-001).
//
// RenderContext serializes every value blocks currently read from ctx via
// core/blocks/context.go. Function-valued context entries (SlotRenderer,
// MediaResolver, EmbedResolver, the generic Queries value) cannot cross the
// wire; see core/docs/wasm-abi.md for how each one maps.
syntax = "proto3";
package abi.v1;
import "google/protobuf/timestamp.proto";
option go_package = "git.dev.alexdunmow.com/block/pluginsdk/abi/v1;abiv1";
// RenderBlockRequest renders one block (blocks.BlockFunc).
message RenderBlockRequest {
string block_key = 1;
// JSON encoding of the block's content map.
bytes content_json = 2;
RenderContext render_context = 3;
}
message RenderBlockResponse {
// Final rendered HTML for a plain (non-template) block. Ignored when
// `powered` is set.
string html = 1;
// Set when the block is "powered": instead of final HTML, the block hands
// back a template string + data map (blocks.PoweredBlock) and the HOST
// renders it (pongo2/ninjatpl, host-side) AFTER this RENDER_BLOCK call has
// returned. Because the block-invoke has already returned, the guest
// instance is free, so any plugin tag/filter the template hits (RENDER_TAG /
// APPLY_FILTER) is a fresh invoke — no re-entrancy. A singular message field
// has explicit presence: nil (GetPowered() == nil) means a plain HTML block.
PoweredBlock powered = 2;
}
// PoweredBlock is a template-backed block result. The host renders `template`
// with `data_json` as the pongo2 data map. See core/docs/wasm-abi.md
// §"Powered blocks (render as a host capability)".
message PoweredBlock {
// ninjatpl/pongo2 template source the host renders host-side.
string template = 1;
// JSON encoding of the template data map (map[string]any).
bytes data_json = 2;
}
// RenderTemplateRequest renders one template (templates.TemplateFunc).
message RenderTemplateRequest {
string template_key = 1;
// JSON encoding of the template's doc map (the page document).
bytes doc_json = 2;
RenderContext render_context = 3;
}
message RenderTemplateResponse {
// Field 1 was `bytes html` — the guest-rendered full document, retired
// flag-day in P3 (host-side chrome composition). Reserved so a stale
// pre-v0.2.0 artifact decodes to an EMPTY document and the host can fail
// that render with an explicit republish error instead of mis-parsing.
reserved 1;
TemplateDocument document = 2;
}
// TemplateDocument is a guest template's contribution to a page. The HOST
// owns the document envelope: it composes doctype/<html>/<head>/<body> with
// the cms-authored bn chrome, built from the same doc map it sent the guest
// as doc_json (see cms docs/superpowers/specs/2026-07-07-host-chrome-injection-design.md).
message TemplateDocument {
bytes body_html = 1; // inner <body> content
string body_class = 2; // <body class="...">
string lang = 3; // <html lang>; empty means host default "en"
bytes head_extra_html = 4; // appended inside <head>, after bn.Head
bytes body_end_extra_html = 5; // appended before </body>, after bn.BodyEnd
// Extra <html>-element attributes (e.g. data-theme). A "lang" key here is
// ignored — the lang field wins.
map<string, string> html_attrs = 6;
}
// RenderTagRequest invokes a plugin-declared template tag (HOOK_RENDER_TAG).
// The host wires one pongo2 tag per manifest.declared_tags name; when its
// engine hits that tag while rendering a powered block, it calls back here.
// Re-entrancy-free: the originating RENDER_BLOCK has already returned.
message RenderTagRequest {
string tag_name = 1;
// JSON encoding of the tag's parsed arguments (map[string]any).
bytes args_json = 2;
// The active render context (same envelope RENDER_BLOCK carries), so the
// tag fn can read page/post/request state via blocks.Get*.
RenderContext render_context = 3;
}
// RenderTagResponse returns the tag's rendered HTML. A non-empty `error`
// carries the tag fn's returned error (distinct from an AbiError, which is
// reserved for transport/dispatch/panic failures).
message RenderTagResponse {
string html = 1;
string error = 2;
}
// ApplyFilterRequest invokes a plugin-declared template filter
// (HOOK_APPLY_FILTER). The host wires one pongo2 filter per
// manifest.declared_filters name.
message ApplyFilterRequest {
string filter_name = 1;
// The value the filter is applied to (pongo2 passes strings).
string input = 2;
// JSON encoding of the filter's arguments (map[string]any).
bytes args_json = 3;
}
// ApplyFilterResponse returns the filtered output. A non-empty `error`
// carries the filter fn's returned error.
message ApplyFilterResponse {
string output = 1;
string error = 2;
}
// RenderContext is the explicit envelope of the context values enumerated
// from core/blocks/context.go. Absent sub-messages mean the corresponding
// ctx value was not set (the getters return nil / zero values).
message RenderContext {
// blocks.GetRequest — subset of *http.Request that render code reads.
RequestInfo request = 1;
// blocks.GetBlockContext — the pongo2 template data struct.
BlockContext block_context = 2;
// blocks.GetTemplateKey.
string template_key = 3;
// blocks.GetCurrentPage.
PageContext page = 4;
// blocks.GetCurrentBlogPost.
PostContext post = 5;
// blocks.GetCurrentAuthor.
AuthorContext author = 6;
// blocks.GetCurrentCategory.
CategoryContext category = 7;
// blocks.GetMasterPage; presence doubles as IsMasterPageContext.
MasterPageContext master_page = 8;
// blocks.GetRequestedPath (404 pages).
string requested_path = 9;
// blocks.GetInjectedSlots (master page rendering).
map<string, string> injected_slots = 10;
// blocks.IsEditor.
bool is_editor = 11;
// blocks.GetExpectedSlots.
repeated string expected_slots = 12;
// blocks.GetBlockID (UUID; empty for uuid.Nil).
string block_id = 13;
// blocks.GetCurrentPageID (UUID; empty for uuid.Nil).
string current_page_id = 14;
// blocks.GetHumanProofBanner.
HumanProofBanner human_proof_banner = 15;
// blocks.GetDetailRow.
DetailRow detail_row = 16;
// Active theme variables, JSON-encoded.
bytes theme_json = 17;
// Site locale (BCP 47).
string locale = 18;
}
// RequestInfo carries the request subset render code reads from
// blocks.GetRequest (single-valued header map, matching BlockContext).
message RequestInfo {
string method = 1;
string url = 2;
string path = 3;
string host = 4;
string raw_query = 5;
map<string, string> headers = 6;
map<string, string> cookies = 7;
string remote_ip = 8;
string referrer = 9;
string user_agent = 10;
}
// PageContext mirrors blocks.PageContext.
message PageContext {
string id = 1; // UUID
string slug = 2;
string title = 3;
string post_type = 4; // "page", "post", "master", "system"
string status = 5; // "published", "draft", "scheduled"
}
// PostContext mirrors blocks.PostContext.
message PostContext {
string id = 1; // UUID
string slug = 2;
string title = 3;
string excerpt = 4;
string featured_image_url = 5;
string author_id = 6; // UUID
google.protobuf.Timestamp published_at = 7;
int32 reading_time = 8;
bool is_featured = 9;
}
// AuthorContext mirrors blocks.AuthorContext.
message AuthorContext {
string id = 1; // UUID
string name = 2;
string slug = 3;
string bio = 4;
string avatar_url = 5;
}
// CategoryContext mirrors blocks.CategoryContext.
message CategoryContext {
string id = 1; // UUID
string name = 2;
string slug = 3;
}
// MasterPageContext mirrors blocks.MasterPageContext.
message MasterPageContext {
string id = 1; // UUID
string slug = 2;
string title = 3;
}
// HumanProofBanner mirrors blocks.HumanProofBannerData.
message HumanProofBanner {
int32 active_time_minutes = 1;
int32 keystroke_count = 2;
int32 session_count = 3;
string post_slug = 4;
}
// DetailRow mirrors blocks.DetailRowInfo.
message DetailRow {
string table_id = 1;
string row_id = 2;
// JSON encoding of the row data map.
bytes data_json = 3;
}
// BlockContext mirrors blocks.BlockContext (the pongo2 data struct) 1:1.
// map[string]any fields travel as JSON bytes.
message BlockContext {
string url = 1;
string path = 2;
string slug = 3;
string page_id = 4;
string page_title = 5;
string template_key = 6;
bool is_editor = 7;
int64 timestamp = 8;
google.protobuf.Timestamp now = 9;
bool is_logged_in = 10;
string user_id = 11;
string user_email = 12;
string user_role = 13;
string method = 14;
string host = 15;
map<string, string> query = 16;
string referrer = 17;
string user_agent = 18;
string ip = 19;
map<string, string> cookies = 20;
map<string, string> headers = 21;
string country = 22;
string city = 23;
string timezone = 24;
bytes current_author_json = 25;
bytes current_post_json = 26;
bytes current_category_json = 27;
bytes site_json = 28;
bool is_public_logged_in = 29;
string public_user_id = 30;
string public_username = 31;
string public_display_name = 32;
bool public_email_verified = 33;
string detail_row_id = 34;
string detail_table_id = 35;
bytes detail_row_data_json = 36;
string blog_index_url = 37;
string category_page_url = 38;
}