pluginsdk/abi/proto/v1/render.proto

310 lines
10 KiB
Protocol Buffer
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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;
// Requested HTTP response status. Before writing headers, the host accepts
// only 200599, uses the highest status class, and keeps the first result
// encountered within that class.
int32 status_code = 3;
// Structured document metadata merged by the host into the page head. The
// first non-empty value for each field wins in deterministic render order.
DocumentMetadata metadata = 4;
}
// DocumentMetadata is the safe, shared subset of document head data a plugin
// may provide. The host validates and escapes every value before rendering.
message DocumentMetadata {
string title = 1;
string description = 2;
string canonical_url = 3;
string image_url = 4;
string robots = 5;
}
// 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;
// Requested HTTP response status. The host applies the same validation and
// precedence rules as PoweredBlock.status_code.
int32 status_code = 7;
// Structured document metadata merged by the host into the page head.
DocumentMetadata metadata = 8;
}
// 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"
// Slug derived from the resolved page hierarchy.
string derived_slug = 6;
// Effective mount path after host inheritance and normalization.
string mount_path = 7;
}
// 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;
// Slug derived from the resolved page hierarchy.
string derived_slug = 39;
// Effective mount path after host inheritance and normalization.
string mount_path = 40;
}