280 lines
9.2 KiB
Protocol Buffer
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;
|
|
}
|