Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f0cc20bbf | ||
|
|
f14846d758 | ||
|
|
e2dcbc1fc3 | ||
|
|
731d6b51d1 | ||
|
|
abef5b70da | ||
|
|
fb58679b6f | ||
|
|
301f286c2f |
@ -770,6 +770,7 @@ message ProvisionerEnsureSettingResponse {}
|
|||||||
// PageSeed mirrors plugin.PageConfig.
|
// PageSeed mirrors plugin.PageConfig.
|
||||||
message PageSeed {
|
message PageSeed {
|
||||||
string slug = 1;
|
string slug = 1;
|
||||||
|
// Legacy slug-based parent reference. Prefer parent_key for new page seeds.
|
||||||
string parent_slug = 2;
|
string parent_slug = 2;
|
||||||
string title = 3;
|
string title = 3;
|
||||||
string template_key = 4;
|
string template_key = 4;
|
||||||
@ -779,6 +780,16 @@ message PageSeed {
|
|||||||
string detail_slug_field = 8;
|
string detail_slug_field = 8;
|
||||||
bool reconcile_blocks = 9;
|
bool reconcile_blocks = 9;
|
||||||
bool reconcile_template = 10;
|
bool reconcile_template = 10;
|
||||||
|
// Stable identity scoped to the declaring plugin.
|
||||||
|
string key = 11;
|
||||||
|
// Stable identity of a parent page declared by the same plugin.
|
||||||
|
string parent_key = 12;
|
||||||
|
// Applied only when the page is created; later reconciliation does not move
|
||||||
|
// an existing page when this value changes.
|
||||||
|
string mount_path = 13;
|
||||||
|
// Shallowly add absent top-level defaults to matching plugin-owned block
|
||||||
|
// assignments while preserving all present values, including empty values.
|
||||||
|
bool merge_missing_block_content = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProvisionerEnsurePageRequest {
|
message ProvisionerEnsurePageRequest {
|
||||||
@ -802,6 +813,10 @@ message ProvisionerEnsureMenuItemRequest {
|
|||||||
string url = 3;
|
string url = 3;
|
||||||
string page_slug = 4;
|
string page_slug = 4;
|
||||||
int32 sort_order = 5;
|
int32 sort_order = 5;
|
||||||
|
// Stable plugin/page-key target. When set, these identify the provisioned
|
||||||
|
// page independently of its current slug.
|
||||||
|
string page_plugin = 6;
|
||||||
|
string page_key = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProvisionerEnsureMenuItemResponse {}
|
message ProvisionerEnsureMenuItemResponse {}
|
||||||
|
|||||||
@ -61,6 +61,9 @@ enum Hook {
|
|||||||
// Run one directory pin decorator (DirectoryExtensions.PinDecorators):
|
// Run one directory pin decorator (DirectoryExtensions.PinDecorators):
|
||||||
// payload = DirectoryPinDecoratorRequest / DirectoryPinDecoratorResponse.
|
// payload = DirectoryPinDecoratorRequest / DirectoryPinDecoratorResponse.
|
||||||
HOOK_DIRECTORY_PIN_DECORATOR = 15;
|
HOOK_DIRECTORY_PIN_DECORATOR = 15;
|
||||||
|
// Run one keyed PluginRegistration.LoadOnce callback:
|
||||||
|
// payload = LoadOnceRequest / LoadOnceResponse.
|
||||||
|
HOOK_LOAD_ONCE = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvokeRequest is the host→guest call envelope.
|
// InvokeRequest is the host→guest call envelope.
|
||||||
@ -145,6 +148,15 @@ message HostConfig {
|
|||||||
|
|
||||||
message LoadResponse {}
|
message LoadResponse {}
|
||||||
|
|
||||||
|
// LoadOnceRequest identifies the callback selected by the host from the
|
||||||
|
// manifest's load_once_keys. The host records the key only after a successful
|
||||||
|
// response; failed callbacks may be retried on a later load.
|
||||||
|
message LoadOnceRequest {
|
||||||
|
string key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LoadOnceResponse {}
|
||||||
|
|
||||||
message UnloadRequest {}
|
message UnloadRequest {}
|
||||||
|
|
||||||
message UnloadResponse {}
|
message UnloadResponse {}
|
||||||
@ -234,6 +246,9 @@ message BridgeCallRequest {
|
|||||||
string service_name = 1;
|
string service_name = 1;
|
||||||
string method = 2;
|
string method = 2;
|
||||||
bytes payload = 3;
|
bytes payload = 3;
|
||||||
|
// Host-authenticated identity of the consumer plugin. The host derives this
|
||||||
|
// from the loaded caller; providers must not trust identity in payload.
|
||||||
|
string caller_plugin = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BridgeCallResponse {
|
message BridgeCallResponse {
|
||||||
|
|||||||
@ -164,6 +164,12 @@ message PluginManifest {
|
|||||||
// every procedure against rbac_method_roles before advertising or invoking
|
// every procedure against rbac_method_roles before advertising or invoking
|
||||||
// it, so descriptor metadata cannot widen the plugin's authorization map.
|
// it, so descriptor metadata cannot widen the plugin's authorization map.
|
||||||
repeated MCPToolDescriptor mcp_tools = 38;
|
repeated MCPToolDescriptor mcp_tools = 38;
|
||||||
|
|
||||||
|
// Stable keys for PluginRegistration.LoadOnce callbacks. The host persists
|
||||||
|
// successful (plugin,key) executions and invokes HOOK_LOAD_ONCE only for
|
||||||
|
// keys it has never completed. Changing a key is the explicit migration
|
||||||
|
// mechanism for running a replacement callback once.
|
||||||
|
repeated string load_once_keys = 39;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MCPToolDescriptor is one unary ConnectRPC method prepared for the host MCP
|
// MCPToolDescriptor is one unary ConnectRPC method prepared for the host MCP
|
||||||
|
|||||||
@ -44,6 +44,23 @@ message PoweredBlock {
|
|||||||
string template = 1;
|
string template = 1;
|
||||||
// JSON encoding of the template data map (map[string]any).
|
// JSON encoding of the template data map (map[string]any).
|
||||||
bytes data_json = 2;
|
bytes data_json = 2;
|
||||||
|
// Requested HTTP response status. Before writing headers, the host accepts
|
||||||
|
// only 200–599, 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).
|
// RenderTemplateRequest renders one template (templates.TemplateFunc).
|
||||||
@ -76,6 +93,11 @@ message TemplateDocument {
|
|||||||
// Extra <html>-element attributes (e.g. data-theme). A "lang" key here is
|
// Extra <html>-element attributes (e.g. data-theme). A "lang" key here is
|
||||||
// ignored — the lang field wins.
|
// ignored — the lang field wins.
|
||||||
map<string, string> html_attrs = 6;
|
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).
|
// RenderTagRequest invokes a plugin-declared template tag (HOOK_RENDER_TAG).
|
||||||
@ -181,6 +203,10 @@ message PageContext {
|
|||||||
string title = 3;
|
string title = 3;
|
||||||
string post_type = 4; // "page", "post", "master", "system"
|
string post_type = 4; // "page", "post", "master", "system"
|
||||||
string status = 5; // "published", "draft", "scheduled"
|
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.
|
// PostContext mirrors blocks.PostContext.
|
||||||
@ -276,4 +302,8 @@ message BlockContext {
|
|||||||
bytes detail_row_data_json = 36;
|
bytes detail_row_data_json = 36;
|
||||||
string blog_index_url = 37;
|
string blog_index_url = 37;
|
||||||
string category_page_url = 38;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6668,19 +6668,30 @@ func (*ProvisionerEnsureSettingResponse) Descriptor() ([]byte, []int) {
|
|||||||
|
|
||||||
// PageSeed mirrors plugin.PageConfig.
|
// PageSeed mirrors plugin.PageConfig.
|
||||||
type PageSeed struct {
|
type PageSeed struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"`
|
Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"`
|
||||||
ParentSlug string `protobuf:"bytes,2,opt,name=parent_slug,json=parentSlug,proto3" json:"parent_slug,omitempty"`
|
// Legacy slug-based parent reference. Prefer parent_key for new page seeds.
|
||||||
Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
|
ParentSlug string `protobuf:"bytes,2,opt,name=parent_slug,json=parentSlug,proto3" json:"parent_slug,omitempty"`
|
||||||
TemplateKey string `protobuf:"bytes,4,opt,name=template_key,json=templateKey,proto3" json:"template_key,omitempty"`
|
Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
|
||||||
Blocks []*PageBlock `protobuf:"bytes,5,rep,name=blocks,proto3" json:"blocks,omitempty"`
|
TemplateKey string `protobuf:"bytes,4,opt,name=template_key,json=templateKey,proto3" json:"template_key,omitempty"`
|
||||||
DetailSourceType string `protobuf:"bytes,6,opt,name=detail_source_type,json=detailSourceType,proto3" json:"detail_source_type,omitempty"`
|
Blocks []*PageBlock `protobuf:"bytes,5,rep,name=blocks,proto3" json:"blocks,omitempty"`
|
||||||
DetailSourceKey string `protobuf:"bytes,7,opt,name=detail_source_key,json=detailSourceKey,proto3" json:"detail_source_key,omitempty"`
|
DetailSourceType string `protobuf:"bytes,6,opt,name=detail_source_type,json=detailSourceType,proto3" json:"detail_source_type,omitempty"`
|
||||||
DetailSlugField string `protobuf:"bytes,8,opt,name=detail_slug_field,json=detailSlugField,proto3" json:"detail_slug_field,omitempty"`
|
DetailSourceKey string `protobuf:"bytes,7,opt,name=detail_source_key,json=detailSourceKey,proto3" json:"detail_source_key,omitempty"`
|
||||||
ReconcileBlocks bool `protobuf:"varint,9,opt,name=reconcile_blocks,json=reconcileBlocks,proto3" json:"reconcile_blocks,omitempty"`
|
DetailSlugField string `protobuf:"bytes,8,opt,name=detail_slug_field,json=detailSlugField,proto3" json:"detail_slug_field,omitempty"`
|
||||||
ReconcileTemplate bool `protobuf:"varint,10,opt,name=reconcile_template,json=reconcileTemplate,proto3" json:"reconcile_template,omitempty"`
|
ReconcileBlocks bool `protobuf:"varint,9,opt,name=reconcile_blocks,json=reconcileBlocks,proto3" json:"reconcile_blocks,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
ReconcileTemplate bool `protobuf:"varint,10,opt,name=reconcile_template,json=reconcileTemplate,proto3" json:"reconcile_template,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
// Stable identity scoped to the declaring plugin.
|
||||||
|
Key string `protobuf:"bytes,11,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
|
// Stable identity of a parent page declared by the same plugin.
|
||||||
|
ParentKey string `protobuf:"bytes,12,opt,name=parent_key,json=parentKey,proto3" json:"parent_key,omitempty"`
|
||||||
|
// Applied only when the page is created; later reconciliation does not move
|
||||||
|
// an existing page when this value changes.
|
||||||
|
MountPath string `protobuf:"bytes,13,opt,name=mount_path,json=mountPath,proto3" json:"mount_path,omitempty"`
|
||||||
|
// Shallowly add absent top-level defaults to matching plugin-owned block
|
||||||
|
// assignments while preserving all present values, including empty values.
|
||||||
|
MergeMissingBlockContent bool `protobuf:"varint,14,opt,name=merge_missing_block_content,json=mergeMissingBlockContent,proto3" json:"merge_missing_block_content,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PageSeed) Reset() {
|
func (x *PageSeed) Reset() {
|
||||||
@ -6783,6 +6794,34 @@ func (x *PageSeed) GetReconcileTemplate() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *PageSeed) GetKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Key
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PageSeed) GetParentKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ParentKey
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PageSeed) GetMountPath() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.MountPath
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PageSeed) GetMergeMissingBlockContent() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.MergeMissingBlockContent
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type ProvisionerEnsurePageRequest struct {
|
type ProvisionerEnsurePageRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Page *PageSeed `protobuf:"bytes,1,opt,name=page,proto3" json:"page,omitempty"`
|
Page *PageSeed `protobuf:"bytes,1,opt,name=page,proto3" json:"page,omitempty"`
|
||||||
@ -6947,12 +6986,16 @@ func (*ProvisionerOverrideSiteSettingsResponse) Descriptor() ([]byte, []int) {
|
|||||||
// ProvisionerEnsureMenuItemRequest mirrors plugin.MenuItemConfig under a
|
// ProvisionerEnsureMenuItemRequest mirrors plugin.MenuItemConfig under a
|
||||||
// named menu.
|
// named menu.
|
||||||
type ProvisionerEnsureMenuItemRequest struct {
|
type ProvisionerEnsureMenuItemRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
MenuName string `protobuf:"bytes,1,opt,name=menu_name,json=menuName,proto3" json:"menu_name,omitempty"`
|
MenuName string `protobuf:"bytes,1,opt,name=menu_name,json=menuName,proto3" json:"menu_name,omitempty"`
|
||||||
Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"`
|
Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"`
|
||||||
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
|
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
|
||||||
PageSlug string `protobuf:"bytes,4,opt,name=page_slug,json=pageSlug,proto3" json:"page_slug,omitempty"`
|
PageSlug string `protobuf:"bytes,4,opt,name=page_slug,json=pageSlug,proto3" json:"page_slug,omitempty"`
|
||||||
SortOrder int32 `protobuf:"varint,5,opt,name=sort_order,json=sortOrder,proto3" json:"sort_order,omitempty"`
|
SortOrder int32 `protobuf:"varint,5,opt,name=sort_order,json=sortOrder,proto3" json:"sort_order,omitempty"`
|
||||||
|
// Stable plugin/page-key target. When set, these identify the provisioned
|
||||||
|
// page independently of its current slug.
|
||||||
|
PagePlugin string `protobuf:"bytes,6,opt,name=page_plugin,json=pagePlugin,proto3" json:"page_plugin,omitempty"`
|
||||||
|
PageKey string `protobuf:"bytes,7,opt,name=page_key,json=pageKey,proto3" json:"page_key,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -7022,6 +7065,20 @@ func (x *ProvisionerEnsureMenuItemRequest) GetSortOrder() int32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *ProvisionerEnsureMenuItemRequest) GetPagePlugin() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PagePlugin
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ProvisionerEnsureMenuItemRequest) GetPageKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PageKey
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type ProvisionerEnsureMenuItemResponse struct {
|
type ProvisionerEnsureMenuItemResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
@ -8786,7 +8843,7 @@ const file_v1_capability_proto_rawDesc = "" +
|
|||||||
"\x1fProvisionerEnsureSettingRequest\x12\x10\n" +
|
"\x1fProvisionerEnsureSettingRequest\x12\x10\n" +
|
||||||
"\x03key\x18\x01 \x01(\tR\x03key\x12,\n" +
|
"\x03key\x18\x01 \x01(\tR\x03key\x12,\n" +
|
||||||
"\x12default_value_json\x18\x02 \x01(\fR\x10defaultValueJson\"\"\n" +
|
"\x12default_value_json\x18\x02 \x01(\fR\x10defaultValueJson\"\"\n" +
|
||||||
" ProvisionerEnsureSettingResponse\"\x83\x03\n" +
|
" ProvisionerEnsureSettingResponse\"\x92\x04\n" +
|
||||||
"\bPageSeed\x12\x12\n" +
|
"\bPageSeed\x12\x12\n" +
|
||||||
"\x04slug\x18\x01 \x01(\tR\x04slug\x12\x1f\n" +
|
"\x04slug\x18\x01 \x01(\tR\x04slug\x12\x1f\n" +
|
||||||
"\vparent_slug\x18\x02 \x01(\tR\n" +
|
"\vparent_slug\x18\x02 \x01(\tR\n" +
|
||||||
@ -8799,20 +8856,29 @@ const file_v1_capability_proto_rawDesc = "" +
|
|||||||
"\x11detail_slug_field\x18\b \x01(\tR\x0fdetailSlugField\x12)\n" +
|
"\x11detail_slug_field\x18\b \x01(\tR\x0fdetailSlugField\x12)\n" +
|
||||||
"\x10reconcile_blocks\x18\t \x01(\bR\x0freconcileBlocks\x12-\n" +
|
"\x10reconcile_blocks\x18\t \x01(\bR\x0freconcileBlocks\x12-\n" +
|
||||||
"\x12reconcile_template\x18\n" +
|
"\x12reconcile_template\x18\n" +
|
||||||
" \x01(\bR\x11reconcileTemplate\"D\n" +
|
" \x01(\bR\x11reconcileTemplate\x12\x10\n" +
|
||||||
|
"\x03key\x18\v \x01(\tR\x03key\x12\x1d\n" +
|
||||||
|
"\n" +
|
||||||
|
"parent_key\x18\f \x01(\tR\tparentKey\x12\x1d\n" +
|
||||||
|
"\n" +
|
||||||
|
"mount_path\x18\r \x01(\tR\tmountPath\x12=\n" +
|
||||||
|
"\x1bmerge_missing_block_content\x18\x0e \x01(\bR\x18mergeMissingBlockContent\"D\n" +
|
||||||
"\x1cProvisionerEnsurePageRequest\x12$\n" +
|
"\x1cProvisionerEnsurePageRequest\x12$\n" +
|
||||||
"\x04page\x18\x01 \x01(\v2\x10.abi.v1.PageSeedR\x04page\"\x1f\n" +
|
"\x04page\x18\x01 \x01(\v2\x10.abi.v1.PageSeedR\x04page\"\x1f\n" +
|
||||||
"\x1dProvisionerEnsurePageResponse\"O\n" +
|
"\x1dProvisionerEnsurePageResponse\"O\n" +
|
||||||
"&ProvisionerOverrideSiteSettingsRequest\x12%\n" +
|
"&ProvisionerOverrideSiteSettingsRequest\x12%\n" +
|
||||||
"\x0eoverrides_json\x18\x01 \x01(\fR\roverridesJson\")\n" +
|
"\x0eoverrides_json\x18\x01 \x01(\fR\roverridesJson\")\n" +
|
||||||
"'ProvisionerOverrideSiteSettingsResponse\"\xa3\x01\n" +
|
"'ProvisionerOverrideSiteSettingsResponse\"\xdf\x01\n" +
|
||||||
" ProvisionerEnsureMenuItemRequest\x12\x1b\n" +
|
" ProvisionerEnsureMenuItemRequest\x12\x1b\n" +
|
||||||
"\tmenu_name\x18\x01 \x01(\tR\bmenuName\x12\x14\n" +
|
"\tmenu_name\x18\x01 \x01(\tR\bmenuName\x12\x14\n" +
|
||||||
"\x05label\x18\x02 \x01(\tR\x05label\x12\x10\n" +
|
"\x05label\x18\x02 \x01(\tR\x05label\x12\x10\n" +
|
||||||
"\x03url\x18\x03 \x01(\tR\x03url\x12\x1b\n" +
|
"\x03url\x18\x03 \x01(\tR\x03url\x12\x1b\n" +
|
||||||
"\tpage_slug\x18\x04 \x01(\tR\bpageSlug\x12\x1d\n" +
|
"\tpage_slug\x18\x04 \x01(\tR\bpageSlug\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"sort_order\x18\x05 \x01(\x05R\tsortOrder\"#\n" +
|
"sort_order\x18\x05 \x01(\x05R\tsortOrder\x12\x1f\n" +
|
||||||
|
"\vpage_plugin\x18\x06 \x01(\tR\n" +
|
||||||
|
"pagePlugin\x12\x19\n" +
|
||||||
|
"\bpage_key\x18\a \x01(\tR\apageKey\"#\n" +
|
||||||
"!ProvisionerEnsureMenuItemResponse\"\x87\x01\n" +
|
"!ProvisionerEnsureMenuItemResponse\"\x87\x01\n" +
|
||||||
")ProvisionerRegisterEmbeddingConfigRequest\x12\x1b\n" +
|
")ProvisionerRegisterEmbeddingConfigRequest\x12\x1b\n" +
|
||||||
"\ttable_key\x18\x01 \x01(\tR\btableKey\x12#\n" +
|
"\ttable_key\x18\x01 \x01(\tR\btableKey\x12#\n" +
|
||||||
|
|||||||
@ -78,6 +78,9 @@ const (
|
|||||||
// Run one directory pin decorator (DirectoryExtensions.PinDecorators):
|
// Run one directory pin decorator (DirectoryExtensions.PinDecorators):
|
||||||
// payload = DirectoryPinDecoratorRequest / DirectoryPinDecoratorResponse.
|
// payload = DirectoryPinDecoratorRequest / DirectoryPinDecoratorResponse.
|
||||||
Hook_HOOK_DIRECTORY_PIN_DECORATOR Hook = 15
|
Hook_HOOK_DIRECTORY_PIN_DECORATOR Hook = 15
|
||||||
|
// Run one keyed PluginRegistration.LoadOnce callback:
|
||||||
|
// payload = LoadOnceRequest / LoadOnceResponse.
|
||||||
|
Hook_HOOK_LOAD_ONCE Hook = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enum value maps for Hook.
|
// Enum value maps for Hook.
|
||||||
@ -99,6 +102,7 @@ var (
|
|||||||
13: "HOOK_BRIDGE_CALL",
|
13: "HOOK_BRIDGE_CALL",
|
||||||
14: "HOOK_DIRECTORY_PANEL_SECTION",
|
14: "HOOK_DIRECTORY_PANEL_SECTION",
|
||||||
15: "HOOK_DIRECTORY_PIN_DECORATOR",
|
15: "HOOK_DIRECTORY_PIN_DECORATOR",
|
||||||
|
16: "HOOK_LOAD_ONCE",
|
||||||
}
|
}
|
||||||
Hook_value = map[string]int32{
|
Hook_value = map[string]int32{
|
||||||
"HOOK_UNSPECIFIED": 0,
|
"HOOK_UNSPECIFIED": 0,
|
||||||
@ -117,6 +121,7 @@ var (
|
|||||||
"HOOK_BRIDGE_CALL": 13,
|
"HOOK_BRIDGE_CALL": 13,
|
||||||
"HOOK_DIRECTORY_PANEL_SECTION": 14,
|
"HOOK_DIRECTORY_PANEL_SECTION": 14,
|
||||||
"HOOK_DIRECTORY_PIN_DECORATOR": 15,
|
"HOOK_DIRECTORY_PIN_DECORATOR": 15,
|
||||||
|
"HOOK_LOAD_ONCE": 16,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -627,6 +632,89 @@ func (*LoadResponse) Descriptor() ([]byte, []int) {
|
|||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{7}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{7}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadOnceRequest identifies the callback selected by the host from the
|
||||||
|
// manifest's load_once_keys. The host records the key only after a successful
|
||||||
|
// response; failed callbacks may be retried on a later load.
|
||||||
|
type LoadOnceRequest struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadOnceRequest) Reset() {
|
||||||
|
*x = LoadOnceRequest{}
|
||||||
|
mi := &file_v1_invoke_proto_msgTypes[8]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadOnceRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LoadOnceRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *LoadOnceRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_v1_invoke_proto_msgTypes[8]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use LoadOnceRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*LoadOnceRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_v1_invoke_proto_rawDescGZIP(), []int{8}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadOnceRequest) GetKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Key
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadOnceResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadOnceResponse) Reset() {
|
||||||
|
*x = LoadOnceResponse{}
|
||||||
|
mi := &file_v1_invoke_proto_msgTypes[9]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadOnceResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LoadOnceResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *LoadOnceResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_v1_invoke_proto_msgTypes[9]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use LoadOnceResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*LoadOnceResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_v1_invoke_proto_rawDescGZIP(), []int{9}
|
||||||
|
}
|
||||||
|
|
||||||
type UnloadRequest struct {
|
type UnloadRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
@ -635,7 +723,7 @@ type UnloadRequest struct {
|
|||||||
|
|
||||||
func (x *UnloadRequest) Reset() {
|
func (x *UnloadRequest) Reset() {
|
||||||
*x = UnloadRequest{}
|
*x = UnloadRequest{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[8]
|
mi := &file_v1_invoke_proto_msgTypes[10]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -647,7 +735,7 @@ func (x *UnloadRequest) String() string {
|
|||||||
func (*UnloadRequest) ProtoMessage() {}
|
func (*UnloadRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *UnloadRequest) ProtoReflect() protoreflect.Message {
|
func (x *UnloadRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[8]
|
mi := &file_v1_invoke_proto_msgTypes[10]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -660,7 +748,7 @@ func (x *UnloadRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use UnloadRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use UnloadRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*UnloadRequest) Descriptor() ([]byte, []int) {
|
func (*UnloadRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{8}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{10}
|
||||||
}
|
}
|
||||||
|
|
||||||
type UnloadResponse struct {
|
type UnloadResponse struct {
|
||||||
@ -671,7 +759,7 @@ type UnloadResponse struct {
|
|||||||
|
|
||||||
func (x *UnloadResponse) Reset() {
|
func (x *UnloadResponse) Reset() {
|
||||||
*x = UnloadResponse{}
|
*x = UnloadResponse{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[9]
|
mi := &file_v1_invoke_proto_msgTypes[11]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -683,7 +771,7 @@ func (x *UnloadResponse) String() string {
|
|||||||
func (*UnloadResponse) ProtoMessage() {}
|
func (*UnloadResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *UnloadResponse) ProtoReflect() protoreflect.Message {
|
func (x *UnloadResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[9]
|
mi := &file_v1_invoke_proto_msgTypes[11]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -696,7 +784,7 @@ func (x *UnloadResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use UnloadResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use UnloadResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*UnloadResponse) Descriptor() ([]byte, []int) {
|
func (*UnloadResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{9}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{11}
|
||||||
}
|
}
|
||||||
|
|
||||||
// JobRequest dispatches one background job to the guest handler registered
|
// JobRequest dispatches one background job to the guest handler registered
|
||||||
@ -712,7 +800,7 @@ type JobRequest struct {
|
|||||||
|
|
||||||
func (x *JobRequest) Reset() {
|
func (x *JobRequest) Reset() {
|
||||||
*x = JobRequest{}
|
*x = JobRequest{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[10]
|
mi := &file_v1_invoke_proto_msgTypes[12]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -724,7 +812,7 @@ func (x *JobRequest) String() string {
|
|||||||
func (*JobRequest) ProtoMessage() {}
|
func (*JobRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *JobRequest) ProtoReflect() protoreflect.Message {
|
func (x *JobRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[10]
|
mi := &file_v1_invoke_proto_msgTypes[12]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -737,7 +825,7 @@ func (x *JobRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use JobRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use JobRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*JobRequest) Descriptor() ([]byte, []int) {
|
func (*JobRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{10}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{12}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *JobRequest) GetJobType() string {
|
func (x *JobRequest) GetJobType() string {
|
||||||
@ -764,7 +852,7 @@ type JobResponse struct {
|
|||||||
|
|
||||||
func (x *JobResponse) Reset() {
|
func (x *JobResponse) Reset() {
|
||||||
*x = JobResponse{}
|
*x = JobResponse{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[11]
|
mi := &file_v1_invoke_proto_msgTypes[13]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -776,7 +864,7 @@ func (x *JobResponse) String() string {
|
|||||||
func (*JobResponse) ProtoMessage() {}
|
func (*JobResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *JobResponse) ProtoReflect() protoreflect.Message {
|
func (x *JobResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[11]
|
mi := &file_v1_invoke_proto_msgTypes[13]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -789,7 +877,7 @@ func (x *JobResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use JobResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use JobResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*JobResponse) Descriptor() ([]byte, []int) {
|
func (*JobResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{11}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{13}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *JobResponse) GetResultJson() []byte {
|
func (x *JobResponse) GetResultJson() []byte {
|
||||||
@ -811,7 +899,7 @@ type RagFetchRequest struct {
|
|||||||
|
|
||||||
func (x *RagFetchRequest) Reset() {
|
func (x *RagFetchRequest) Reset() {
|
||||||
*x = RagFetchRequest{}
|
*x = RagFetchRequest{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[12]
|
mi := &file_v1_invoke_proto_msgTypes[14]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -823,7 +911,7 @@ func (x *RagFetchRequest) String() string {
|
|||||||
func (*RagFetchRequest) ProtoMessage() {}
|
func (*RagFetchRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RagFetchRequest) ProtoReflect() protoreflect.Message {
|
func (x *RagFetchRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[12]
|
mi := &file_v1_invoke_proto_msgTypes[14]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -836,7 +924,7 @@ func (x *RagFetchRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RagFetchRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RagFetchRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*RagFetchRequest) Descriptor() ([]byte, []int) {
|
func (*RagFetchRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{12}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{14}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RagFetchRequest) GetContentType() string {
|
func (x *RagFetchRequest) GetContentType() string {
|
||||||
@ -864,7 +952,7 @@ type RagFetchResponse struct {
|
|||||||
|
|
||||||
func (x *RagFetchResponse) Reset() {
|
func (x *RagFetchResponse) Reset() {
|
||||||
*x = RagFetchResponse{}
|
*x = RagFetchResponse{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[13]
|
mi := &file_v1_invoke_proto_msgTypes[15]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -876,7 +964,7 @@ func (x *RagFetchResponse) String() string {
|
|||||||
func (*RagFetchResponse) ProtoMessage() {}
|
func (*RagFetchResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RagFetchResponse) ProtoReflect() protoreflect.Message {
|
func (x *RagFetchResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[13]
|
mi := &file_v1_invoke_proto_msgTypes[15]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -889,7 +977,7 @@ func (x *RagFetchResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RagFetchResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RagFetchResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*RagFetchResponse) Descriptor() ([]byte, []int) {
|
func (*RagFetchResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{13}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{15}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RagFetchResponse) GetTitle() string {
|
func (x *RagFetchResponse) GetTitle() string {
|
||||||
@ -921,7 +1009,7 @@ type MediaHookRequest struct {
|
|||||||
|
|
||||||
func (x *MediaHookRequest) Reset() {
|
func (x *MediaHookRequest) Reset() {
|
||||||
*x = MediaHookRequest{}
|
*x = MediaHookRequest{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[14]
|
mi := &file_v1_invoke_proto_msgTypes[16]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -933,7 +1021,7 @@ func (x *MediaHookRequest) String() string {
|
|||||||
func (*MediaHookRequest) ProtoMessage() {}
|
func (*MediaHookRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *MediaHookRequest) ProtoReflect() protoreflect.Message {
|
func (x *MediaHookRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[14]
|
mi := &file_v1_invoke_proto_msgTypes[16]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -946,7 +1034,7 @@ func (x *MediaHookRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use MediaHookRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use MediaHookRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*MediaHookRequest) Descriptor() ([]byte, []int) {
|
func (*MediaHookRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{14}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{16}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *MediaHookRequest) GetEvent() isMediaHookRequest_Event {
|
func (x *MediaHookRequest) GetEvent() isMediaHookRequest_Event {
|
||||||
@ -998,7 +1086,7 @@ type MediaHookResponse struct {
|
|||||||
|
|
||||||
func (x *MediaHookResponse) Reset() {
|
func (x *MediaHookResponse) Reset() {
|
||||||
*x = MediaHookResponse{}
|
*x = MediaHookResponse{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[15]
|
mi := &file_v1_invoke_proto_msgTypes[17]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1010,7 +1098,7 @@ func (x *MediaHookResponse) String() string {
|
|||||||
func (*MediaHookResponse) ProtoMessage() {}
|
func (*MediaHookResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *MediaHookResponse) ProtoReflect() protoreflect.Message {
|
func (x *MediaHookResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[15]
|
mi := &file_v1_invoke_proto_msgTypes[17]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1023,7 +1111,7 @@ func (x *MediaHookResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use MediaHookResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use MediaHookResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*MediaHookResponse) Descriptor() ([]byte, []int) {
|
func (*MediaHookResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{15}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{17}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MediaAnalyzedEvent mirrors plugin.MediaAnalyzedEvent (UUIDs as strings).
|
// MediaAnalyzedEvent mirrors plugin.MediaAnalyzedEvent (UUIDs as strings).
|
||||||
@ -1045,7 +1133,7 @@ type MediaAnalyzedEvent struct {
|
|||||||
|
|
||||||
func (x *MediaAnalyzedEvent) Reset() {
|
func (x *MediaAnalyzedEvent) Reset() {
|
||||||
*x = MediaAnalyzedEvent{}
|
*x = MediaAnalyzedEvent{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[16]
|
mi := &file_v1_invoke_proto_msgTypes[18]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1057,7 +1145,7 @@ func (x *MediaAnalyzedEvent) String() string {
|
|||||||
func (*MediaAnalyzedEvent) ProtoMessage() {}
|
func (*MediaAnalyzedEvent) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *MediaAnalyzedEvent) ProtoReflect() protoreflect.Message {
|
func (x *MediaAnalyzedEvent) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[16]
|
mi := &file_v1_invoke_proto_msgTypes[18]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1070,7 +1158,7 @@ func (x *MediaAnalyzedEvent) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use MediaAnalyzedEvent.ProtoReflect.Descriptor instead.
|
// Deprecated: Use MediaAnalyzedEvent.ProtoReflect.Descriptor instead.
|
||||||
func (*MediaAnalyzedEvent) Descriptor() ([]byte, []int) {
|
func (*MediaAnalyzedEvent) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{16}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{18}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *MediaAnalyzedEvent) GetMediaId() string {
|
func (x *MediaAnalyzedEvent) GetMediaId() string {
|
||||||
@ -1156,7 +1244,7 @@ type AiToolCallRequest struct {
|
|||||||
|
|
||||||
func (x *AiToolCallRequest) Reset() {
|
func (x *AiToolCallRequest) Reset() {
|
||||||
*x = AiToolCallRequest{}
|
*x = AiToolCallRequest{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[17]
|
mi := &file_v1_invoke_proto_msgTypes[19]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1168,7 +1256,7 @@ func (x *AiToolCallRequest) String() string {
|
|||||||
func (*AiToolCallRequest) ProtoMessage() {}
|
func (*AiToolCallRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *AiToolCallRequest) ProtoReflect() protoreflect.Message {
|
func (x *AiToolCallRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[17]
|
mi := &file_v1_invoke_proto_msgTypes[19]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1181,7 +1269,7 @@ func (x *AiToolCallRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use AiToolCallRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use AiToolCallRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*AiToolCallRequest) Descriptor() ([]byte, []int) {
|
func (*AiToolCallRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{17}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{19}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *AiToolCallRequest) GetSlug() string {
|
func (x *AiToolCallRequest) GetSlug() string {
|
||||||
@ -1211,7 +1299,7 @@ type AiToolCallResponse struct {
|
|||||||
|
|
||||||
func (x *AiToolCallResponse) Reset() {
|
func (x *AiToolCallResponse) Reset() {
|
||||||
*x = AiToolCallResponse{}
|
*x = AiToolCallResponse{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[18]
|
mi := &file_v1_invoke_proto_msgTypes[20]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1223,7 +1311,7 @@ func (x *AiToolCallResponse) String() string {
|
|||||||
func (*AiToolCallResponse) ProtoMessage() {}
|
func (*AiToolCallResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *AiToolCallResponse) ProtoReflect() protoreflect.Message {
|
func (x *AiToolCallResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[18]
|
mi := &file_v1_invoke_proto_msgTypes[20]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1236,7 +1324,7 @@ func (x *AiToolCallResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use AiToolCallResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use AiToolCallResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*AiToolCallResponse) Descriptor() ([]byte, []int) {
|
func (*AiToolCallResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{18}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{20}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *AiToolCallResponse) GetContent() string {
|
func (x *AiToolCallResponse) GetContent() string {
|
||||||
@ -1258,17 +1346,20 @@ func (x *AiToolCallResponse) GetErrorMessage() string {
|
|||||||
// capability; payload encoding is agreed between the two plugins (JSON by
|
// capability; payload encoding is agreed between the two plugins (JSON by
|
||||||
// convention).
|
// convention).
|
||||||
type BridgeCallRequest struct {
|
type BridgeCallRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
ServiceName string `protobuf:"bytes,1,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"`
|
ServiceName string `protobuf:"bytes,1,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"`
|
||||||
Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"`
|
Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"`
|
||||||
Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
|
Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||||
|
// Host-authenticated identity of the consumer plugin. The host derives this
|
||||||
|
// from the loaded caller; providers must not trust identity in payload.
|
||||||
|
CallerPlugin string `protobuf:"bytes,4,opt,name=caller_plugin,json=callerPlugin,proto3" json:"caller_plugin,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *BridgeCallRequest) Reset() {
|
func (x *BridgeCallRequest) Reset() {
|
||||||
*x = BridgeCallRequest{}
|
*x = BridgeCallRequest{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[19]
|
mi := &file_v1_invoke_proto_msgTypes[21]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1280,7 +1371,7 @@ func (x *BridgeCallRequest) String() string {
|
|||||||
func (*BridgeCallRequest) ProtoMessage() {}
|
func (*BridgeCallRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *BridgeCallRequest) ProtoReflect() protoreflect.Message {
|
func (x *BridgeCallRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[19]
|
mi := &file_v1_invoke_proto_msgTypes[21]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1293,7 +1384,7 @@ func (x *BridgeCallRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use BridgeCallRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use BridgeCallRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*BridgeCallRequest) Descriptor() ([]byte, []int) {
|
func (*BridgeCallRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{19}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{21}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *BridgeCallRequest) GetServiceName() string {
|
func (x *BridgeCallRequest) GetServiceName() string {
|
||||||
@ -1317,6 +1408,13 @@ func (x *BridgeCallRequest) GetPayload() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *BridgeCallRequest) GetCallerPlugin() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.CallerPlugin
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type BridgeCallResponse struct {
|
type BridgeCallResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
|
Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||||
@ -1326,7 +1424,7 @@ type BridgeCallResponse struct {
|
|||||||
|
|
||||||
func (x *BridgeCallResponse) Reset() {
|
func (x *BridgeCallResponse) Reset() {
|
||||||
*x = BridgeCallResponse{}
|
*x = BridgeCallResponse{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[20]
|
mi := &file_v1_invoke_proto_msgTypes[22]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1338,7 +1436,7 @@ func (x *BridgeCallResponse) String() string {
|
|||||||
func (*BridgeCallResponse) ProtoMessage() {}
|
func (*BridgeCallResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *BridgeCallResponse) ProtoReflect() protoreflect.Message {
|
func (x *BridgeCallResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[20]
|
mi := &file_v1_invoke_proto_msgTypes[22]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1351,7 +1449,7 @@ func (x *BridgeCallResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use BridgeCallResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use BridgeCallResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*BridgeCallResponse) Descriptor() ([]byte, []int) {
|
func (*BridgeCallResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{20}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{22}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *BridgeCallResponse) GetPayload() []byte {
|
func (x *BridgeCallResponse) GetPayload() []byte {
|
||||||
@ -1375,7 +1473,7 @@ type DirectoryPanelSectionRequest struct {
|
|||||||
|
|
||||||
func (x *DirectoryPanelSectionRequest) Reset() {
|
func (x *DirectoryPanelSectionRequest) Reset() {
|
||||||
*x = DirectoryPanelSectionRequest{}
|
*x = DirectoryPanelSectionRequest{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[21]
|
mi := &file_v1_invoke_proto_msgTypes[23]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1387,7 +1485,7 @@ func (x *DirectoryPanelSectionRequest) String() string {
|
|||||||
func (*DirectoryPanelSectionRequest) ProtoMessage() {}
|
func (*DirectoryPanelSectionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *DirectoryPanelSectionRequest) ProtoReflect() protoreflect.Message {
|
func (x *DirectoryPanelSectionRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[21]
|
mi := &file_v1_invoke_proto_msgTypes[23]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1400,7 +1498,7 @@ func (x *DirectoryPanelSectionRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use DirectoryPanelSectionRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use DirectoryPanelSectionRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*DirectoryPanelSectionRequest) Descriptor() ([]byte, []int) {
|
func (*DirectoryPanelSectionRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{21}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{23}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DirectoryPanelSectionRequest) GetIndex() uint32 {
|
func (x *DirectoryPanelSectionRequest) GetIndex() uint32 {
|
||||||
@ -1426,7 +1524,7 @@ type DirectoryPanelSectionResponse struct {
|
|||||||
|
|
||||||
func (x *DirectoryPanelSectionResponse) Reset() {
|
func (x *DirectoryPanelSectionResponse) Reset() {
|
||||||
*x = DirectoryPanelSectionResponse{}
|
*x = DirectoryPanelSectionResponse{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[22]
|
mi := &file_v1_invoke_proto_msgTypes[24]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1438,7 +1536,7 @@ func (x *DirectoryPanelSectionResponse) String() string {
|
|||||||
func (*DirectoryPanelSectionResponse) ProtoMessage() {}
|
func (*DirectoryPanelSectionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *DirectoryPanelSectionResponse) ProtoReflect() protoreflect.Message {
|
func (x *DirectoryPanelSectionResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[22]
|
mi := &file_v1_invoke_proto_msgTypes[24]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1451,7 +1549,7 @@ func (x *DirectoryPanelSectionResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use DirectoryPanelSectionResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use DirectoryPanelSectionResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*DirectoryPanelSectionResponse) Descriptor() ([]byte, []int) {
|
func (*DirectoryPanelSectionResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{22}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{24}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DirectoryPanelSectionResponse) GetHtml() string {
|
func (x *DirectoryPanelSectionResponse) GetHtml() string {
|
||||||
@ -1476,7 +1574,7 @@ type DirectoryPinDecoratorRequest struct {
|
|||||||
|
|
||||||
func (x *DirectoryPinDecoratorRequest) Reset() {
|
func (x *DirectoryPinDecoratorRequest) Reset() {
|
||||||
*x = DirectoryPinDecoratorRequest{}
|
*x = DirectoryPinDecoratorRequest{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[23]
|
mi := &file_v1_invoke_proto_msgTypes[25]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1488,7 +1586,7 @@ func (x *DirectoryPinDecoratorRequest) String() string {
|
|||||||
func (*DirectoryPinDecoratorRequest) ProtoMessage() {}
|
func (*DirectoryPinDecoratorRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *DirectoryPinDecoratorRequest) ProtoReflect() protoreflect.Message {
|
func (x *DirectoryPinDecoratorRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[23]
|
mi := &file_v1_invoke_proto_msgTypes[25]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1501,7 +1599,7 @@ func (x *DirectoryPinDecoratorRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use DirectoryPinDecoratorRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use DirectoryPinDecoratorRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*DirectoryPinDecoratorRequest) Descriptor() ([]byte, []int) {
|
func (*DirectoryPinDecoratorRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{23}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{25}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DirectoryPinDecoratorRequest) GetIndex() uint32 {
|
func (x *DirectoryPinDecoratorRequest) GetIndex() uint32 {
|
||||||
@ -1535,7 +1633,7 @@ type DirectoryPinDecoratorResponse struct {
|
|||||||
|
|
||||||
func (x *DirectoryPinDecoratorResponse) Reset() {
|
func (x *DirectoryPinDecoratorResponse) Reset() {
|
||||||
*x = DirectoryPinDecoratorResponse{}
|
*x = DirectoryPinDecoratorResponse{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[24]
|
mi := &file_v1_invoke_proto_msgTypes[26]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1547,7 +1645,7 @@ func (x *DirectoryPinDecoratorResponse) String() string {
|
|||||||
func (*DirectoryPinDecoratorResponse) ProtoMessage() {}
|
func (*DirectoryPinDecoratorResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *DirectoryPinDecoratorResponse) ProtoReflect() protoreflect.Message {
|
func (x *DirectoryPinDecoratorResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[24]
|
mi := &file_v1_invoke_proto_msgTypes[26]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1560,7 +1658,7 @@ func (x *DirectoryPinDecoratorResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use DirectoryPinDecoratorResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use DirectoryPinDecoratorResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*DirectoryPinDecoratorResponse) Descriptor() ([]byte, []int) {
|
func (*DirectoryPinDecoratorResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{24}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{26}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DirectoryPinDecoratorResponse) GetPinJson() []byte {
|
func (x *DirectoryPinDecoratorResponse) GetPinJson() []byte {
|
||||||
@ -1588,7 +1686,7 @@ type ModerationDecisionEvent struct {
|
|||||||
|
|
||||||
func (x *ModerationDecisionEvent) Reset() {
|
func (x *ModerationDecisionEvent) Reset() {
|
||||||
*x = ModerationDecisionEvent{}
|
*x = ModerationDecisionEvent{}
|
||||||
mi := &file_v1_invoke_proto_msgTypes[25]
|
mi := &file_v1_invoke_proto_msgTypes[27]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1600,7 +1698,7 @@ func (x *ModerationDecisionEvent) String() string {
|
|||||||
func (*ModerationDecisionEvent) ProtoMessage() {}
|
func (*ModerationDecisionEvent) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ModerationDecisionEvent) ProtoReflect() protoreflect.Message {
|
func (x *ModerationDecisionEvent) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_invoke_proto_msgTypes[25]
|
mi := &file_v1_invoke_proto_msgTypes[27]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1613,7 +1711,7 @@ func (x *ModerationDecisionEvent) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use ModerationDecisionEvent.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ModerationDecisionEvent.ProtoReflect.Descriptor instead.
|
||||||
func (*ModerationDecisionEvent) Descriptor() ([]byte, []int) {
|
func (*ModerationDecisionEvent) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_invoke_proto_rawDescGZIP(), []int{25}
|
return file_v1_invoke_proto_rawDescGZIP(), []int{27}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ModerationDecisionEvent) GetMediaId() string {
|
func (x *ModerationDecisionEvent) GetMediaId() string {
|
||||||
@ -1707,7 +1805,10 @@ const file_v1_invoke_proto_rawDesc = "" +
|
|||||||
"\aapp_url\x18\x01 \x01(\tR\x06appUrl\x12\x1d\n" +
|
"\aapp_url\x18\x01 \x01(\tR\x06appUrl\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"media_path\x18\x02 \x01(\tR\tmediaPath\"\x0e\n" +
|
"media_path\x18\x02 \x01(\tR\tmediaPath\"\x0e\n" +
|
||||||
"\fLoadResponse\"\x0f\n" +
|
"\fLoadResponse\"#\n" +
|
||||||
|
"\x0fLoadOnceRequest\x12\x10\n" +
|
||||||
|
"\x03key\x18\x01 \x01(\tR\x03key\"\x12\n" +
|
||||||
|
"\x10LoadOnceResponse\"\x0f\n" +
|
||||||
"\rUnloadRequest\"\x10\n" +
|
"\rUnloadRequest\"\x10\n" +
|
||||||
"\x0eUnloadResponse\"H\n" +
|
"\x0eUnloadResponse\"H\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
@ -1751,11 +1852,12 @@ const file_v1_invoke_proto_rawDesc = "" +
|
|||||||
"paramsJson\"S\n" +
|
"paramsJson\"S\n" +
|
||||||
"\x12AiToolCallResponse\x12\x18\n" +
|
"\x12AiToolCallResponse\x12\x18\n" +
|
||||||
"\acontent\x18\x01 \x01(\tR\acontent\x12#\n" +
|
"\acontent\x18\x01 \x01(\tR\acontent\x12#\n" +
|
||||||
"\rerror_message\x18\x02 \x01(\tR\ferrorMessage\"h\n" +
|
"\rerror_message\x18\x02 \x01(\tR\ferrorMessage\"\x8d\x01\n" +
|
||||||
"\x11BridgeCallRequest\x12!\n" +
|
"\x11BridgeCallRequest\x12!\n" +
|
||||||
"\fservice_name\x18\x01 \x01(\tR\vserviceName\x12\x16\n" +
|
"\fservice_name\x18\x01 \x01(\tR\vserviceName\x12\x16\n" +
|
||||||
"\x06method\x18\x02 \x01(\tR\x06method\x12\x18\n" +
|
"\x06method\x18\x02 \x01(\tR\x06method\x12\x18\n" +
|
||||||
"\apayload\x18\x03 \x01(\fR\apayload\".\n" +
|
"\apayload\x18\x03 \x01(\fR\apayload\x12#\n" +
|
||||||
|
"\rcaller_plugin\x18\x04 \x01(\tR\fcallerPlugin\".\n" +
|
||||||
"\x12BridgeCallResponse\x12\x18\n" +
|
"\x12BridgeCallResponse\x12\x18\n" +
|
||||||
"\apayload\x18\x01 \x01(\fR\apayload\"Q\n" +
|
"\apayload\x18\x01 \x01(\fR\apayload\"Q\n" +
|
||||||
"\x1cDirectoryPanelSectionRequest\x12\x14\n" +
|
"\x1cDirectoryPanelSectionRequest\x12\x14\n" +
|
||||||
@ -1780,7 +1882,7 @@ const file_v1_invoke_proto_rawDesc = "" +
|
|||||||
"sourceType\x12\"\n" +
|
"sourceType\x12\"\n" +
|
||||||
"\rsource_ref_id\x18\a \x01(\tR\vsourceRefId\x12!\n" +
|
"\rsource_ref_id\x18\a \x01(\tR\vsourceRefId\x12!\n" +
|
||||||
"\fmoderated_by\x18\b \x01(\tR\vmoderatedBy\x12\x12\n" +
|
"\fmoderated_by\x18\b \x01(\tR\vmoderatedBy\x12\x12\n" +
|
||||||
"\x04note\x18\t \x01(\tR\x04note*\xea\x02\n" +
|
"\x04note\x18\t \x01(\tR\x04note*\xfe\x02\n" +
|
||||||
"\x04Hook\x12\x14\n" +
|
"\x04Hook\x12\x14\n" +
|
||||||
"\x10HOOK_UNSPECIFIED\x10\x00\x12\x15\n" +
|
"\x10HOOK_UNSPECIFIED\x10\x00\x12\x15\n" +
|
||||||
"\x11HOOK_RENDER_BLOCK\x10\x01\x12\x18\n" +
|
"\x11HOOK_RENDER_BLOCK\x10\x01\x12\x18\n" +
|
||||||
@ -1798,7 +1900,8 @@ const file_v1_invoke_proto_rawDesc = "" +
|
|||||||
"\x11HOOK_AI_TOOL_CALL\x10\f\x12\x14\n" +
|
"\x11HOOK_AI_TOOL_CALL\x10\f\x12\x14\n" +
|
||||||
"\x10HOOK_BRIDGE_CALL\x10\r\x12 \n" +
|
"\x10HOOK_BRIDGE_CALL\x10\r\x12 \n" +
|
||||||
"\x1cHOOK_DIRECTORY_PANEL_SECTION\x10\x0e\x12 \n" +
|
"\x1cHOOK_DIRECTORY_PANEL_SECTION\x10\x0e\x12 \n" +
|
||||||
"\x1cHOOK_DIRECTORY_PIN_DECORATOR\x10\x0f*\x95\x02\n" +
|
"\x1cHOOK_DIRECTORY_PIN_DECORATOR\x10\x0f\x12\x12\n" +
|
||||||
|
"\x0eHOOK_LOAD_ONCE\x10\x10*\x95\x02\n" +
|
||||||
"\fAbiErrorCode\x12\x1e\n" +
|
"\fAbiErrorCode\x12\x1e\n" +
|
||||||
"\x1aABI_ERROR_CODE_UNSPECIFIED\x10\x00\x12\x1b\n" +
|
"\x1aABI_ERROR_CODE_UNSPECIFIED\x10\x00\x12\x1b\n" +
|
||||||
"\x17ABI_ERROR_CODE_INTERNAL\x10\x01\x12\x19\n" +
|
"\x17ABI_ERROR_CODE_INTERNAL\x10\x01\x12\x19\n" +
|
||||||
@ -1822,7 +1925,7 @@ func file_v1_invoke_proto_rawDescGZIP() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var file_v1_invoke_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
var file_v1_invoke_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||||
var file_v1_invoke_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
|
var file_v1_invoke_proto_msgTypes = make([]protoimpl.MessageInfo, 28)
|
||||||
var file_v1_invoke_proto_goTypes = []any{
|
var file_v1_invoke_proto_goTypes = []any{
|
||||||
(Hook)(0), // 0: abi.v1.Hook
|
(Hook)(0), // 0: abi.v1.Hook
|
||||||
(AbiErrorCode)(0), // 1: abi.v1.AbiErrorCode
|
(AbiErrorCode)(0), // 1: abi.v1.AbiErrorCode
|
||||||
@ -1834,34 +1937,36 @@ var file_v1_invoke_proto_goTypes = []any{
|
|||||||
(*LoadRequest)(nil), // 7: abi.v1.LoadRequest
|
(*LoadRequest)(nil), // 7: abi.v1.LoadRequest
|
||||||
(*HostConfig)(nil), // 8: abi.v1.HostConfig
|
(*HostConfig)(nil), // 8: abi.v1.HostConfig
|
||||||
(*LoadResponse)(nil), // 9: abi.v1.LoadResponse
|
(*LoadResponse)(nil), // 9: abi.v1.LoadResponse
|
||||||
(*UnloadRequest)(nil), // 10: abi.v1.UnloadRequest
|
(*LoadOnceRequest)(nil), // 10: abi.v1.LoadOnceRequest
|
||||||
(*UnloadResponse)(nil), // 11: abi.v1.UnloadResponse
|
(*LoadOnceResponse)(nil), // 11: abi.v1.LoadOnceResponse
|
||||||
(*JobRequest)(nil), // 12: abi.v1.JobRequest
|
(*UnloadRequest)(nil), // 12: abi.v1.UnloadRequest
|
||||||
(*JobResponse)(nil), // 13: abi.v1.JobResponse
|
(*UnloadResponse)(nil), // 13: abi.v1.UnloadResponse
|
||||||
(*RagFetchRequest)(nil), // 14: abi.v1.RagFetchRequest
|
(*JobRequest)(nil), // 14: abi.v1.JobRequest
|
||||||
(*RagFetchResponse)(nil), // 15: abi.v1.RagFetchResponse
|
(*JobResponse)(nil), // 15: abi.v1.JobResponse
|
||||||
(*MediaHookRequest)(nil), // 16: abi.v1.MediaHookRequest
|
(*RagFetchRequest)(nil), // 16: abi.v1.RagFetchRequest
|
||||||
(*MediaHookResponse)(nil), // 17: abi.v1.MediaHookResponse
|
(*RagFetchResponse)(nil), // 17: abi.v1.RagFetchResponse
|
||||||
(*MediaAnalyzedEvent)(nil), // 18: abi.v1.MediaAnalyzedEvent
|
(*MediaHookRequest)(nil), // 18: abi.v1.MediaHookRequest
|
||||||
(*AiToolCallRequest)(nil), // 19: abi.v1.AiToolCallRequest
|
(*MediaHookResponse)(nil), // 19: abi.v1.MediaHookResponse
|
||||||
(*AiToolCallResponse)(nil), // 20: abi.v1.AiToolCallResponse
|
(*MediaAnalyzedEvent)(nil), // 20: abi.v1.MediaAnalyzedEvent
|
||||||
(*BridgeCallRequest)(nil), // 21: abi.v1.BridgeCallRequest
|
(*AiToolCallRequest)(nil), // 21: abi.v1.AiToolCallRequest
|
||||||
(*BridgeCallResponse)(nil), // 22: abi.v1.BridgeCallResponse
|
(*AiToolCallResponse)(nil), // 22: abi.v1.AiToolCallResponse
|
||||||
(*DirectoryPanelSectionRequest)(nil), // 23: abi.v1.DirectoryPanelSectionRequest
|
(*BridgeCallRequest)(nil), // 23: abi.v1.BridgeCallRequest
|
||||||
(*DirectoryPanelSectionResponse)(nil), // 24: abi.v1.DirectoryPanelSectionResponse
|
(*BridgeCallResponse)(nil), // 24: abi.v1.BridgeCallResponse
|
||||||
(*DirectoryPinDecoratorRequest)(nil), // 25: abi.v1.DirectoryPinDecoratorRequest
|
(*DirectoryPanelSectionRequest)(nil), // 25: abi.v1.DirectoryPanelSectionRequest
|
||||||
(*DirectoryPinDecoratorResponse)(nil), // 26: abi.v1.DirectoryPinDecoratorResponse
|
(*DirectoryPanelSectionResponse)(nil), // 26: abi.v1.DirectoryPanelSectionResponse
|
||||||
(*ModerationDecisionEvent)(nil), // 27: abi.v1.ModerationDecisionEvent
|
(*DirectoryPinDecoratorRequest)(nil), // 27: abi.v1.DirectoryPinDecoratorRequest
|
||||||
(*PluginManifest)(nil), // 28: abi.v1.PluginManifest
|
(*DirectoryPinDecoratorResponse)(nil), // 28: abi.v1.DirectoryPinDecoratorResponse
|
||||||
|
(*ModerationDecisionEvent)(nil), // 29: abi.v1.ModerationDecisionEvent
|
||||||
|
(*PluginManifest)(nil), // 30: abi.v1.PluginManifest
|
||||||
}
|
}
|
||||||
var file_v1_invoke_proto_depIdxs = []int32{
|
var file_v1_invoke_proto_depIdxs = []int32{
|
||||||
0, // 0: abi.v1.InvokeRequest.hook:type_name -> abi.v1.Hook
|
0, // 0: abi.v1.InvokeRequest.hook:type_name -> abi.v1.Hook
|
||||||
4, // 1: abi.v1.InvokeResponse.error:type_name -> abi.v1.AbiError
|
4, // 1: abi.v1.InvokeResponse.error:type_name -> abi.v1.AbiError
|
||||||
1, // 2: abi.v1.AbiError.code:type_name -> abi.v1.AbiErrorCode
|
1, // 2: abi.v1.AbiError.code:type_name -> abi.v1.AbiErrorCode
|
||||||
28, // 3: abi.v1.DescribeResponse.manifest:type_name -> abi.v1.PluginManifest
|
30, // 3: abi.v1.DescribeResponse.manifest:type_name -> abi.v1.PluginManifest
|
||||||
8, // 4: abi.v1.LoadRequest.host_config:type_name -> abi.v1.HostConfig
|
8, // 4: abi.v1.LoadRequest.host_config:type_name -> abi.v1.HostConfig
|
||||||
18, // 5: abi.v1.MediaHookRequest.media_analyzed:type_name -> abi.v1.MediaAnalyzedEvent
|
20, // 5: abi.v1.MediaHookRequest.media_analyzed:type_name -> abi.v1.MediaAnalyzedEvent
|
||||||
27, // 6: abi.v1.MediaHookRequest.moderation_decision:type_name -> abi.v1.ModerationDecisionEvent
|
29, // 6: abi.v1.MediaHookRequest.moderation_decision:type_name -> abi.v1.ModerationDecisionEvent
|
||||||
7, // [7:7] is the sub-list for method output_type
|
7, // [7:7] is the sub-list for method output_type
|
||||||
7, // [7:7] is the sub-list for method input_type
|
7, // [7:7] is the sub-list for method input_type
|
||||||
7, // [7:7] is the sub-list for extension type_name
|
7, // [7:7] is the sub-list for extension type_name
|
||||||
@ -1875,7 +1980,7 @@ func file_v1_invoke_proto_init() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
file_v1_manifest_proto_init()
|
file_v1_manifest_proto_init()
|
||||||
file_v1_invoke_proto_msgTypes[14].OneofWrappers = []any{
|
file_v1_invoke_proto_msgTypes[16].OneofWrappers = []any{
|
||||||
(*MediaHookRequest_MediaAnalyzed)(nil),
|
(*MediaHookRequest_MediaAnalyzed)(nil),
|
||||||
(*MediaHookRequest_ModerationDecision)(nil),
|
(*MediaHookRequest_ModerationDecision)(nil),
|
||||||
}
|
}
|
||||||
@ -1885,7 +1990,7 @@ func file_v1_invoke_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1_invoke_proto_rawDesc), len(file_v1_invoke_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1_invoke_proto_rawDesc), len(file_v1_invoke_proto_rawDesc)),
|
||||||
NumEnums: 2,
|
NumEnums: 2,
|
||||||
NumMessages: 26,
|
NumMessages: 28,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -156,7 +156,12 @@ type PluginManifest struct {
|
|||||||
// duplicate schemas or procedure metadata by hand. The host still checks
|
// duplicate schemas or procedure metadata by hand. The host still checks
|
||||||
// every procedure against rbac_method_roles before advertising or invoking
|
// every procedure against rbac_method_roles before advertising or invoking
|
||||||
// it, so descriptor metadata cannot widen the plugin's authorization map.
|
// it, so descriptor metadata cannot widen the plugin's authorization map.
|
||||||
McpTools []*MCPToolDescriptor `protobuf:"bytes,38,rep,name=mcp_tools,json=mcpTools,proto3" json:"mcp_tools,omitempty"`
|
McpTools []*MCPToolDescriptor `protobuf:"bytes,38,rep,name=mcp_tools,json=mcpTools,proto3" json:"mcp_tools,omitempty"`
|
||||||
|
// Stable keys for PluginRegistration.LoadOnce callbacks. The host persists
|
||||||
|
// successful (plugin,key) executions and invokes HOOK_LOAD_ONCE only for
|
||||||
|
// keys it has never completed. Changing a key is the explicit migration
|
||||||
|
// mechanism for running a replacement callback once.
|
||||||
|
LoadOnceKeys []string `protobuf:"bytes,39,rep,name=load_once_keys,json=loadOnceKeys,proto3" json:"load_once_keys,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -457,6 +462,13 @@ func (x *PluginManifest) GetMcpTools() []*MCPToolDescriptor {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *PluginManifest) GetLoadOnceKeys() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.LoadOnceKeys
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MCPToolDescriptor is one unary ConnectRPC method prepared for the host MCP
|
// MCPToolDescriptor is one unary ConnectRPC method prepared for the host MCP
|
||||||
// catalogue. Procedure, service, method, documentation, and the request JSON
|
// catalogue. Procedure, service, method, documentation, and the request JSON
|
||||||
// schema are all derived from protobuf reflection during DESCRIBE.
|
// schema are all derived from protobuf reflection during DESCRIBE.
|
||||||
@ -1524,7 +1536,7 @@ var File_v1_manifest_proto protoreflect.FileDescriptor
|
|||||||
|
|
||||||
const file_v1_manifest_proto_rawDesc = "" +
|
const file_v1_manifest_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x11v1/manifest.proto\x12\x06abi.v1\"\xe7\x0e\n" +
|
"\x11v1/manifest.proto\x12\x06abi.v1\"\x8d\x0f\n" +
|
||||||
"\x0ePluginManifest\x12\x1f\n" +
|
"\x0ePluginManifest\x12\x1f\n" +
|
||||||
"\vabi_version\x18\x01 \x01(\rR\n" +
|
"\vabi_version\x18\x01 \x01(\rR\n" +
|
||||||
"abiVersion\x12\x12\n" +
|
"abiVersion\x12\x12\n" +
|
||||||
@ -1567,7 +1579,8 @@ const file_v1_manifest_proto_rawDesc = "" +
|
|||||||
"\x0fmax_response_mb\x18# \x01(\rR\rmaxResponseMb\x128\n" +
|
"\x0fmax_response_mb\x18# \x01(\rR\rmaxResponseMb\x128\n" +
|
||||||
"\rpublic_routes\x18$ \x03(\v2\x13.abi.v1.PublicRouteR\fpublicRoutes\x12\x18\n" +
|
"\rpublic_routes\x18$ \x03(\v2\x13.abi.v1.PublicRouteR\fpublicRoutes\x12\x18\n" +
|
||||||
"\asitemap\x18% \x01(\bR\asitemap\x126\n" +
|
"\asitemap\x18% \x01(\bR\asitemap\x126\n" +
|
||||||
"\tmcp_tools\x18& \x03(\v2\x19.abi.v1.MCPToolDescriptorR\bmcpTools\x1aB\n" +
|
"\tmcp_tools\x18& \x03(\v2\x19.abi.v1.MCPToolDescriptorR\bmcpTools\x12$\n" +
|
||||||
|
"\x0eload_once_keys\x18' \x03(\tR\floadOnceKeys\x1aB\n" +
|
||||||
"\x14RbacMethodRolesEntry\x12\x10\n" +
|
"\x14RbacMethodRolesEntry\x12\x10\n" +
|
||||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xf2\x01\n" +
|
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xf2\x01\n" +
|
||||||
|
|||||||
@ -161,7 +161,14 @@ type PoweredBlock struct {
|
|||||||
// ninjatpl/pongo2 template source the host renders host-side.
|
// ninjatpl/pongo2 template source the host renders host-side.
|
||||||
Template string `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"`
|
Template string `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"`
|
||||||
// JSON encoding of the template data map (map[string]any).
|
// JSON encoding of the template data map (map[string]any).
|
||||||
DataJson []byte `protobuf:"bytes,2,opt,name=data_json,json=dataJson,proto3" json:"data_json,omitempty"`
|
DataJson []byte `protobuf:"bytes,2,opt,name=data_json,json=dataJson,proto3" json:"data_json,omitempty"`
|
||||||
|
// Requested HTTP response status. Before writing headers, the host accepts
|
||||||
|
// only 200–599, uses the highest status class, and keeps the first result
|
||||||
|
// encountered within that class.
|
||||||
|
StatusCode int32 `protobuf:"varint,3,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"`
|
||||||
|
// Structured document metadata merged by the host into the page head. The
|
||||||
|
// first non-empty value for each field wins in deterministic render order.
|
||||||
|
Metadata *DocumentMetadata `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -210,6 +217,98 @@ func (x *PoweredBlock) GetDataJson() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *PoweredBlock) GetStatusCode() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.StatusCode
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PoweredBlock) GetMetadata() *DocumentMetadata {
|
||||||
|
if x != nil {
|
||||||
|
return x.Metadata
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentMetadata is the safe, shared subset of document head data a plugin
|
||||||
|
// may provide. The host validates and escapes every value before rendering.
|
||||||
|
type DocumentMetadata struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
|
||||||
|
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
|
||||||
|
CanonicalUrl string `protobuf:"bytes,3,opt,name=canonical_url,json=canonicalUrl,proto3" json:"canonical_url,omitempty"`
|
||||||
|
ImageUrl string `protobuf:"bytes,4,opt,name=image_url,json=imageUrl,proto3" json:"image_url,omitempty"`
|
||||||
|
Robots string `protobuf:"bytes,5,opt,name=robots,proto3" json:"robots,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DocumentMetadata) Reset() {
|
||||||
|
*x = DocumentMetadata{}
|
||||||
|
mi := &file_v1_render_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DocumentMetadata) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*DocumentMetadata) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *DocumentMetadata) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_v1_render_proto_msgTypes[3]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use DocumentMetadata.ProtoReflect.Descriptor instead.
|
||||||
|
func (*DocumentMetadata) Descriptor() ([]byte, []int) {
|
||||||
|
return file_v1_render_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DocumentMetadata) GetTitle() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Title
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DocumentMetadata) GetDescription() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Description
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DocumentMetadata) GetCanonicalUrl() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.CanonicalUrl
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DocumentMetadata) GetImageUrl() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ImageUrl
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DocumentMetadata) GetRobots() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Robots
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// RenderTemplateRequest renders one template (templates.TemplateFunc).
|
// RenderTemplateRequest renders one template (templates.TemplateFunc).
|
||||||
type RenderTemplateRequest struct {
|
type RenderTemplateRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
@ -223,7 +322,7 @@ type RenderTemplateRequest struct {
|
|||||||
|
|
||||||
func (x *RenderTemplateRequest) Reset() {
|
func (x *RenderTemplateRequest) Reset() {
|
||||||
*x = RenderTemplateRequest{}
|
*x = RenderTemplateRequest{}
|
||||||
mi := &file_v1_render_proto_msgTypes[3]
|
mi := &file_v1_render_proto_msgTypes[4]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -235,7 +334,7 @@ func (x *RenderTemplateRequest) String() string {
|
|||||||
func (*RenderTemplateRequest) ProtoMessage() {}
|
func (*RenderTemplateRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RenderTemplateRequest) ProtoReflect() protoreflect.Message {
|
func (x *RenderTemplateRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[3]
|
mi := &file_v1_render_proto_msgTypes[4]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -248,7 +347,7 @@ func (x *RenderTemplateRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RenderTemplateRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RenderTemplateRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*RenderTemplateRequest) Descriptor() ([]byte, []int) {
|
func (*RenderTemplateRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{3}
|
return file_v1_render_proto_rawDescGZIP(), []int{4}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RenderTemplateRequest) GetTemplateKey() string {
|
func (x *RenderTemplateRequest) GetTemplateKey() string {
|
||||||
@ -281,7 +380,7 @@ type RenderTemplateResponse struct {
|
|||||||
|
|
||||||
func (x *RenderTemplateResponse) Reset() {
|
func (x *RenderTemplateResponse) Reset() {
|
||||||
*x = RenderTemplateResponse{}
|
*x = RenderTemplateResponse{}
|
||||||
mi := &file_v1_render_proto_msgTypes[4]
|
mi := &file_v1_render_proto_msgTypes[5]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -293,7 +392,7 @@ func (x *RenderTemplateResponse) String() string {
|
|||||||
func (*RenderTemplateResponse) ProtoMessage() {}
|
func (*RenderTemplateResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RenderTemplateResponse) ProtoReflect() protoreflect.Message {
|
func (x *RenderTemplateResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[4]
|
mi := &file_v1_render_proto_msgTypes[5]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -306,7 +405,7 @@ func (x *RenderTemplateResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RenderTemplateResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RenderTemplateResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*RenderTemplateResponse) Descriptor() ([]byte, []int) {
|
func (*RenderTemplateResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{4}
|
return file_v1_render_proto_rawDescGZIP(), []int{5}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RenderTemplateResponse) GetDocument() *TemplateDocument {
|
func (x *RenderTemplateResponse) GetDocument() *TemplateDocument {
|
||||||
@ -329,14 +428,19 @@ type TemplateDocument struct {
|
|||||||
BodyEndExtraHtml []byte `protobuf:"bytes,5,opt,name=body_end_extra_html,json=bodyEndExtraHtml,proto3" json:"body_end_extra_html,omitempty"` // appended before </body>, after bn.BodyEnd
|
BodyEndExtraHtml []byte `protobuf:"bytes,5,opt,name=body_end_extra_html,json=bodyEndExtraHtml,proto3" json:"body_end_extra_html,omitempty"` // appended before </body>, after bn.BodyEnd
|
||||||
// Extra <html>-element attributes (e.g. data-theme). A "lang" key here is
|
// Extra <html>-element attributes (e.g. data-theme). A "lang" key here is
|
||||||
// ignored — the lang field wins.
|
// ignored — the lang field wins.
|
||||||
HtmlAttrs map[string]string `protobuf:"bytes,6,rep,name=html_attrs,json=htmlAttrs,proto3" json:"html_attrs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
HtmlAttrs map[string]string `protobuf:"bytes,6,rep,name=html_attrs,json=htmlAttrs,proto3" json:"html_attrs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||||
|
// Requested HTTP response status. The host applies the same validation and
|
||||||
|
// precedence rules as PoweredBlock.status_code.
|
||||||
|
StatusCode int32 `protobuf:"varint,7,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"`
|
||||||
|
// Structured document metadata merged by the host into the page head.
|
||||||
|
Metadata *DocumentMetadata `protobuf:"bytes,8,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *TemplateDocument) Reset() {
|
func (x *TemplateDocument) Reset() {
|
||||||
*x = TemplateDocument{}
|
*x = TemplateDocument{}
|
||||||
mi := &file_v1_render_proto_msgTypes[5]
|
mi := &file_v1_render_proto_msgTypes[6]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -348,7 +452,7 @@ func (x *TemplateDocument) String() string {
|
|||||||
func (*TemplateDocument) ProtoMessage() {}
|
func (*TemplateDocument) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *TemplateDocument) ProtoReflect() protoreflect.Message {
|
func (x *TemplateDocument) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[5]
|
mi := &file_v1_render_proto_msgTypes[6]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -361,7 +465,7 @@ func (x *TemplateDocument) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use TemplateDocument.ProtoReflect.Descriptor instead.
|
// Deprecated: Use TemplateDocument.ProtoReflect.Descriptor instead.
|
||||||
func (*TemplateDocument) Descriptor() ([]byte, []int) {
|
func (*TemplateDocument) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{5}
|
return file_v1_render_proto_rawDescGZIP(), []int{6}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *TemplateDocument) GetBodyHtml() []byte {
|
func (x *TemplateDocument) GetBodyHtml() []byte {
|
||||||
@ -406,6 +510,20 @@ func (x *TemplateDocument) GetHtmlAttrs() map[string]string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *TemplateDocument) GetStatusCode() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.StatusCode
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TemplateDocument) GetMetadata() *DocumentMetadata {
|
||||||
|
if x != nil {
|
||||||
|
return x.Metadata
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RenderTagRequest invokes a plugin-declared template tag (HOOK_RENDER_TAG).
|
// RenderTagRequest invokes a plugin-declared template tag (HOOK_RENDER_TAG).
|
||||||
// The host wires one pongo2 tag per manifest.declared_tags name; when its
|
// 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.
|
// engine hits that tag while rendering a powered block, it calls back here.
|
||||||
@ -424,7 +542,7 @@ type RenderTagRequest struct {
|
|||||||
|
|
||||||
func (x *RenderTagRequest) Reset() {
|
func (x *RenderTagRequest) Reset() {
|
||||||
*x = RenderTagRequest{}
|
*x = RenderTagRequest{}
|
||||||
mi := &file_v1_render_proto_msgTypes[6]
|
mi := &file_v1_render_proto_msgTypes[7]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -436,7 +554,7 @@ func (x *RenderTagRequest) String() string {
|
|||||||
func (*RenderTagRequest) ProtoMessage() {}
|
func (*RenderTagRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RenderTagRequest) ProtoReflect() protoreflect.Message {
|
func (x *RenderTagRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[6]
|
mi := &file_v1_render_proto_msgTypes[7]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -449,7 +567,7 @@ func (x *RenderTagRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RenderTagRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RenderTagRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*RenderTagRequest) Descriptor() ([]byte, []int) {
|
func (*RenderTagRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{6}
|
return file_v1_render_proto_rawDescGZIP(), []int{7}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RenderTagRequest) GetTagName() string {
|
func (x *RenderTagRequest) GetTagName() string {
|
||||||
@ -486,7 +604,7 @@ type RenderTagResponse struct {
|
|||||||
|
|
||||||
func (x *RenderTagResponse) Reset() {
|
func (x *RenderTagResponse) Reset() {
|
||||||
*x = RenderTagResponse{}
|
*x = RenderTagResponse{}
|
||||||
mi := &file_v1_render_proto_msgTypes[7]
|
mi := &file_v1_render_proto_msgTypes[8]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -498,7 +616,7 @@ func (x *RenderTagResponse) String() string {
|
|||||||
func (*RenderTagResponse) ProtoMessage() {}
|
func (*RenderTagResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RenderTagResponse) ProtoReflect() protoreflect.Message {
|
func (x *RenderTagResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[7]
|
mi := &file_v1_render_proto_msgTypes[8]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -511,7 +629,7 @@ func (x *RenderTagResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RenderTagResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RenderTagResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*RenderTagResponse) Descriptor() ([]byte, []int) {
|
func (*RenderTagResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{7}
|
return file_v1_render_proto_rawDescGZIP(), []int{8}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RenderTagResponse) GetHtml() string {
|
func (x *RenderTagResponse) GetHtml() string {
|
||||||
@ -544,7 +662,7 @@ type ApplyFilterRequest struct {
|
|||||||
|
|
||||||
func (x *ApplyFilterRequest) Reset() {
|
func (x *ApplyFilterRequest) Reset() {
|
||||||
*x = ApplyFilterRequest{}
|
*x = ApplyFilterRequest{}
|
||||||
mi := &file_v1_render_proto_msgTypes[8]
|
mi := &file_v1_render_proto_msgTypes[9]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -556,7 +674,7 @@ func (x *ApplyFilterRequest) String() string {
|
|||||||
func (*ApplyFilterRequest) ProtoMessage() {}
|
func (*ApplyFilterRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ApplyFilterRequest) ProtoReflect() protoreflect.Message {
|
func (x *ApplyFilterRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[8]
|
mi := &file_v1_render_proto_msgTypes[9]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -569,7 +687,7 @@ func (x *ApplyFilterRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use ApplyFilterRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ApplyFilterRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*ApplyFilterRequest) Descriptor() ([]byte, []int) {
|
func (*ApplyFilterRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{8}
|
return file_v1_render_proto_rawDescGZIP(), []int{9}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ApplyFilterRequest) GetFilterName() string {
|
func (x *ApplyFilterRequest) GetFilterName() string {
|
||||||
@ -605,7 +723,7 @@ type ApplyFilterResponse struct {
|
|||||||
|
|
||||||
func (x *ApplyFilterResponse) Reset() {
|
func (x *ApplyFilterResponse) Reset() {
|
||||||
*x = ApplyFilterResponse{}
|
*x = ApplyFilterResponse{}
|
||||||
mi := &file_v1_render_proto_msgTypes[9]
|
mi := &file_v1_render_proto_msgTypes[10]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -617,7 +735,7 @@ func (x *ApplyFilterResponse) String() string {
|
|||||||
func (*ApplyFilterResponse) ProtoMessage() {}
|
func (*ApplyFilterResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ApplyFilterResponse) ProtoReflect() protoreflect.Message {
|
func (x *ApplyFilterResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[9]
|
mi := &file_v1_render_proto_msgTypes[10]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -630,7 +748,7 @@ func (x *ApplyFilterResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use ApplyFilterResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ApplyFilterResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*ApplyFilterResponse) Descriptor() ([]byte, []int) {
|
func (*ApplyFilterResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{9}
|
return file_v1_render_proto_rawDescGZIP(), []int{10}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ApplyFilterResponse) GetOutput() string {
|
func (x *ApplyFilterResponse) GetOutput() string {
|
||||||
@ -694,7 +812,7 @@ type RenderContext struct {
|
|||||||
|
|
||||||
func (x *RenderContext) Reset() {
|
func (x *RenderContext) Reset() {
|
||||||
*x = RenderContext{}
|
*x = RenderContext{}
|
||||||
mi := &file_v1_render_proto_msgTypes[10]
|
mi := &file_v1_render_proto_msgTypes[11]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -706,7 +824,7 @@ func (x *RenderContext) String() string {
|
|||||||
func (*RenderContext) ProtoMessage() {}
|
func (*RenderContext) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RenderContext) ProtoReflect() protoreflect.Message {
|
func (x *RenderContext) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[10]
|
mi := &file_v1_render_proto_msgTypes[11]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -719,7 +837,7 @@ func (x *RenderContext) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RenderContext.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RenderContext.ProtoReflect.Descriptor instead.
|
||||||
func (*RenderContext) Descriptor() ([]byte, []int) {
|
func (*RenderContext) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{10}
|
return file_v1_render_proto_rawDescGZIP(), []int{11}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RenderContext) GetRequest() *RequestInfo {
|
func (x *RenderContext) GetRequest() *RequestInfo {
|
||||||
@ -868,7 +986,7 @@ type RequestInfo struct {
|
|||||||
|
|
||||||
func (x *RequestInfo) Reset() {
|
func (x *RequestInfo) Reset() {
|
||||||
*x = RequestInfo{}
|
*x = RequestInfo{}
|
||||||
mi := &file_v1_render_proto_msgTypes[11]
|
mi := &file_v1_render_proto_msgTypes[12]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -880,7 +998,7 @@ func (x *RequestInfo) String() string {
|
|||||||
func (*RequestInfo) ProtoMessage() {}
|
func (*RequestInfo) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RequestInfo) ProtoReflect() protoreflect.Message {
|
func (x *RequestInfo) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[11]
|
mi := &file_v1_render_proto_msgTypes[12]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -893,7 +1011,7 @@ func (x *RequestInfo) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RequestInfo.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RequestInfo.ProtoReflect.Descriptor instead.
|
||||||
func (*RequestInfo) Descriptor() ([]byte, []int) {
|
func (*RequestInfo) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{11}
|
return file_v1_render_proto_rawDescGZIP(), []int{12}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RequestInfo) GetMethod() string {
|
func (x *RequestInfo) GetMethod() string {
|
||||||
@ -968,19 +1086,23 @@ func (x *RequestInfo) GetUserAgent() string {
|
|||||||
|
|
||||||
// PageContext mirrors blocks.PageContext.
|
// PageContext mirrors blocks.PageContext.
|
||||||
type PageContext struct {
|
type PageContext struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // UUID
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // UUID
|
||||||
Slug string `protobuf:"bytes,2,opt,name=slug,proto3" json:"slug,omitempty"`
|
Slug string `protobuf:"bytes,2,opt,name=slug,proto3" json:"slug,omitempty"`
|
||||||
Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
|
Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
|
||||||
PostType string `protobuf:"bytes,4,opt,name=post_type,json=postType,proto3" json:"post_type,omitempty"` // "page", "post", "master", "system"
|
PostType string `protobuf:"bytes,4,opt,name=post_type,json=postType,proto3" json:"post_type,omitempty"` // "page", "post", "master", "system"
|
||||||
Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` // "published", "draft", "scheduled"
|
Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` // "published", "draft", "scheduled"
|
||||||
|
// Slug derived from the resolved page hierarchy.
|
||||||
|
DerivedSlug string `protobuf:"bytes,6,opt,name=derived_slug,json=derivedSlug,proto3" json:"derived_slug,omitempty"`
|
||||||
|
// Effective mount path after host inheritance and normalization.
|
||||||
|
MountPath string `protobuf:"bytes,7,opt,name=mount_path,json=mountPath,proto3" json:"mount_path,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PageContext) Reset() {
|
func (x *PageContext) Reset() {
|
||||||
*x = PageContext{}
|
*x = PageContext{}
|
||||||
mi := &file_v1_render_proto_msgTypes[12]
|
mi := &file_v1_render_proto_msgTypes[13]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -992,7 +1114,7 @@ func (x *PageContext) String() string {
|
|||||||
func (*PageContext) ProtoMessage() {}
|
func (*PageContext) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *PageContext) ProtoReflect() protoreflect.Message {
|
func (x *PageContext) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[12]
|
mi := &file_v1_render_proto_msgTypes[13]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1005,7 +1127,7 @@ func (x *PageContext) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use PageContext.ProtoReflect.Descriptor instead.
|
// Deprecated: Use PageContext.ProtoReflect.Descriptor instead.
|
||||||
func (*PageContext) Descriptor() ([]byte, []int) {
|
func (*PageContext) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{12}
|
return file_v1_render_proto_rawDescGZIP(), []int{13}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PageContext) GetId() string {
|
func (x *PageContext) GetId() string {
|
||||||
@ -1043,6 +1165,20 @@ func (x *PageContext) GetStatus() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *PageContext) GetDerivedSlug() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.DerivedSlug
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PageContext) GetMountPath() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.MountPath
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// PostContext mirrors blocks.PostContext.
|
// PostContext mirrors blocks.PostContext.
|
||||||
type PostContext struct {
|
type PostContext struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
@ -1061,7 +1197,7 @@ type PostContext struct {
|
|||||||
|
|
||||||
func (x *PostContext) Reset() {
|
func (x *PostContext) Reset() {
|
||||||
*x = PostContext{}
|
*x = PostContext{}
|
||||||
mi := &file_v1_render_proto_msgTypes[13]
|
mi := &file_v1_render_proto_msgTypes[14]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1073,7 +1209,7 @@ func (x *PostContext) String() string {
|
|||||||
func (*PostContext) ProtoMessage() {}
|
func (*PostContext) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *PostContext) ProtoReflect() protoreflect.Message {
|
func (x *PostContext) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[13]
|
mi := &file_v1_render_proto_msgTypes[14]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1086,7 +1222,7 @@ func (x *PostContext) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use PostContext.ProtoReflect.Descriptor instead.
|
// Deprecated: Use PostContext.ProtoReflect.Descriptor instead.
|
||||||
func (*PostContext) Descriptor() ([]byte, []int) {
|
func (*PostContext) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{13}
|
return file_v1_render_proto_rawDescGZIP(), []int{14}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PostContext) GetId() string {
|
func (x *PostContext) GetId() string {
|
||||||
@ -1166,7 +1302,7 @@ type AuthorContext struct {
|
|||||||
|
|
||||||
func (x *AuthorContext) Reset() {
|
func (x *AuthorContext) Reset() {
|
||||||
*x = AuthorContext{}
|
*x = AuthorContext{}
|
||||||
mi := &file_v1_render_proto_msgTypes[14]
|
mi := &file_v1_render_proto_msgTypes[15]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1178,7 +1314,7 @@ func (x *AuthorContext) String() string {
|
|||||||
func (*AuthorContext) ProtoMessage() {}
|
func (*AuthorContext) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *AuthorContext) ProtoReflect() protoreflect.Message {
|
func (x *AuthorContext) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[14]
|
mi := &file_v1_render_proto_msgTypes[15]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1191,7 +1327,7 @@ func (x *AuthorContext) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use AuthorContext.ProtoReflect.Descriptor instead.
|
// Deprecated: Use AuthorContext.ProtoReflect.Descriptor instead.
|
||||||
func (*AuthorContext) Descriptor() ([]byte, []int) {
|
func (*AuthorContext) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{14}
|
return file_v1_render_proto_rawDescGZIP(), []int{15}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *AuthorContext) GetId() string {
|
func (x *AuthorContext) GetId() string {
|
||||||
@ -1241,7 +1377,7 @@ type CategoryContext struct {
|
|||||||
|
|
||||||
func (x *CategoryContext) Reset() {
|
func (x *CategoryContext) Reset() {
|
||||||
*x = CategoryContext{}
|
*x = CategoryContext{}
|
||||||
mi := &file_v1_render_proto_msgTypes[15]
|
mi := &file_v1_render_proto_msgTypes[16]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1253,7 +1389,7 @@ func (x *CategoryContext) String() string {
|
|||||||
func (*CategoryContext) ProtoMessage() {}
|
func (*CategoryContext) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *CategoryContext) ProtoReflect() protoreflect.Message {
|
func (x *CategoryContext) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[15]
|
mi := &file_v1_render_proto_msgTypes[16]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1266,7 +1402,7 @@ func (x *CategoryContext) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use CategoryContext.ProtoReflect.Descriptor instead.
|
// Deprecated: Use CategoryContext.ProtoReflect.Descriptor instead.
|
||||||
func (*CategoryContext) Descriptor() ([]byte, []int) {
|
func (*CategoryContext) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{15}
|
return file_v1_render_proto_rawDescGZIP(), []int{16}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *CategoryContext) GetId() string {
|
func (x *CategoryContext) GetId() string {
|
||||||
@ -1302,7 +1438,7 @@ type MasterPageContext struct {
|
|||||||
|
|
||||||
func (x *MasterPageContext) Reset() {
|
func (x *MasterPageContext) Reset() {
|
||||||
*x = MasterPageContext{}
|
*x = MasterPageContext{}
|
||||||
mi := &file_v1_render_proto_msgTypes[16]
|
mi := &file_v1_render_proto_msgTypes[17]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1314,7 +1450,7 @@ func (x *MasterPageContext) String() string {
|
|||||||
func (*MasterPageContext) ProtoMessage() {}
|
func (*MasterPageContext) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *MasterPageContext) ProtoReflect() protoreflect.Message {
|
func (x *MasterPageContext) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[16]
|
mi := &file_v1_render_proto_msgTypes[17]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1327,7 +1463,7 @@ func (x *MasterPageContext) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use MasterPageContext.ProtoReflect.Descriptor instead.
|
// Deprecated: Use MasterPageContext.ProtoReflect.Descriptor instead.
|
||||||
func (*MasterPageContext) Descriptor() ([]byte, []int) {
|
func (*MasterPageContext) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{16}
|
return file_v1_render_proto_rawDescGZIP(), []int{17}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *MasterPageContext) GetId() string {
|
func (x *MasterPageContext) GetId() string {
|
||||||
@ -1364,7 +1500,7 @@ type HumanProofBanner struct {
|
|||||||
|
|
||||||
func (x *HumanProofBanner) Reset() {
|
func (x *HumanProofBanner) Reset() {
|
||||||
*x = HumanProofBanner{}
|
*x = HumanProofBanner{}
|
||||||
mi := &file_v1_render_proto_msgTypes[17]
|
mi := &file_v1_render_proto_msgTypes[18]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1376,7 +1512,7 @@ func (x *HumanProofBanner) String() string {
|
|||||||
func (*HumanProofBanner) ProtoMessage() {}
|
func (*HumanProofBanner) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *HumanProofBanner) ProtoReflect() protoreflect.Message {
|
func (x *HumanProofBanner) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[17]
|
mi := &file_v1_render_proto_msgTypes[18]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1389,7 +1525,7 @@ func (x *HumanProofBanner) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use HumanProofBanner.ProtoReflect.Descriptor instead.
|
// Deprecated: Use HumanProofBanner.ProtoReflect.Descriptor instead.
|
||||||
func (*HumanProofBanner) Descriptor() ([]byte, []int) {
|
func (*HumanProofBanner) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{17}
|
return file_v1_render_proto_rawDescGZIP(), []int{18}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *HumanProofBanner) GetActiveTimeMinutes() int32 {
|
func (x *HumanProofBanner) GetActiveTimeMinutes() int32 {
|
||||||
@ -1433,7 +1569,7 @@ type DetailRow struct {
|
|||||||
|
|
||||||
func (x *DetailRow) Reset() {
|
func (x *DetailRow) Reset() {
|
||||||
*x = DetailRow{}
|
*x = DetailRow{}
|
||||||
mi := &file_v1_render_proto_msgTypes[18]
|
mi := &file_v1_render_proto_msgTypes[19]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1445,7 +1581,7 @@ func (x *DetailRow) String() string {
|
|||||||
func (*DetailRow) ProtoMessage() {}
|
func (*DetailRow) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *DetailRow) ProtoReflect() protoreflect.Message {
|
func (x *DetailRow) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[18]
|
mi := &file_v1_render_proto_msgTypes[19]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1458,7 +1594,7 @@ func (x *DetailRow) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use DetailRow.ProtoReflect.Descriptor instead.
|
// Deprecated: Use DetailRow.ProtoReflect.Descriptor instead.
|
||||||
func (*DetailRow) Descriptor() ([]byte, []int) {
|
func (*DetailRow) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{18}
|
return file_v1_render_proto_rawDescGZIP(), []int{19}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DetailRow) GetTableId() string {
|
func (x *DetailRow) GetTableId() string {
|
||||||
@ -1524,13 +1660,17 @@ type BlockContext struct {
|
|||||||
DetailRowDataJson []byte `protobuf:"bytes,36,opt,name=detail_row_data_json,json=detailRowDataJson,proto3" json:"detail_row_data_json,omitempty"`
|
DetailRowDataJson []byte `protobuf:"bytes,36,opt,name=detail_row_data_json,json=detailRowDataJson,proto3" json:"detail_row_data_json,omitempty"`
|
||||||
BlogIndexUrl string `protobuf:"bytes,37,opt,name=blog_index_url,json=blogIndexUrl,proto3" json:"blog_index_url,omitempty"`
|
BlogIndexUrl string `protobuf:"bytes,37,opt,name=blog_index_url,json=blogIndexUrl,proto3" json:"blog_index_url,omitempty"`
|
||||||
CategoryPageUrl string `protobuf:"bytes,38,opt,name=category_page_url,json=categoryPageUrl,proto3" json:"category_page_url,omitempty"`
|
CategoryPageUrl string `protobuf:"bytes,38,opt,name=category_page_url,json=categoryPageUrl,proto3" json:"category_page_url,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
// Slug derived from the resolved page hierarchy.
|
||||||
sizeCache protoimpl.SizeCache
|
DerivedSlug string `protobuf:"bytes,39,opt,name=derived_slug,json=derivedSlug,proto3" json:"derived_slug,omitempty"`
|
||||||
|
// Effective mount path after host inheritance and normalization.
|
||||||
|
MountPath string `protobuf:"bytes,40,opt,name=mount_path,json=mountPath,proto3" json:"mount_path,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *BlockContext) Reset() {
|
func (x *BlockContext) Reset() {
|
||||||
*x = BlockContext{}
|
*x = BlockContext{}
|
||||||
mi := &file_v1_render_proto_msgTypes[19]
|
mi := &file_v1_render_proto_msgTypes[20]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -1542,7 +1682,7 @@ func (x *BlockContext) String() string {
|
|||||||
func (*BlockContext) ProtoMessage() {}
|
func (*BlockContext) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *BlockContext) ProtoReflect() protoreflect.Message {
|
func (x *BlockContext) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_v1_render_proto_msgTypes[19]
|
mi := &file_v1_render_proto_msgTypes[20]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -1555,7 +1695,7 @@ func (x *BlockContext) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use BlockContext.ProtoReflect.Descriptor instead.
|
// Deprecated: Use BlockContext.ProtoReflect.Descriptor instead.
|
||||||
func (*BlockContext) Descriptor() ([]byte, []int) {
|
func (*BlockContext) Descriptor() ([]byte, []int) {
|
||||||
return file_v1_render_proto_rawDescGZIP(), []int{19}
|
return file_v1_render_proto_rawDescGZIP(), []int{20}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *BlockContext) GetUrl() string {
|
func (x *BlockContext) GetUrl() string {
|
||||||
@ -1824,6 +1964,20 @@ func (x *BlockContext) GetCategoryPageUrl() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *BlockContext) GetDerivedSlug() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.DerivedSlug
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *BlockContext) GetMountPath() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.MountPath
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var File_v1_render_proto protoreflect.FileDescriptor
|
var File_v1_render_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_v1_render_proto_rawDesc = "" +
|
const file_v1_render_proto_rawDesc = "" +
|
||||||
@ -1835,16 +1989,25 @@ const file_v1_render_proto_rawDesc = "" +
|
|||||||
"\x0erender_context\x18\x03 \x01(\v2\x15.abi.v1.RenderContextR\rrenderContext\"Y\n" +
|
"\x0erender_context\x18\x03 \x01(\v2\x15.abi.v1.RenderContextR\rrenderContext\"Y\n" +
|
||||||
"\x13RenderBlockResponse\x12\x12\n" +
|
"\x13RenderBlockResponse\x12\x12\n" +
|
||||||
"\x04html\x18\x01 \x01(\tR\x04html\x12.\n" +
|
"\x04html\x18\x01 \x01(\tR\x04html\x12.\n" +
|
||||||
"\apowered\x18\x02 \x01(\v2\x14.abi.v1.PoweredBlockR\apowered\"G\n" +
|
"\apowered\x18\x02 \x01(\v2\x14.abi.v1.PoweredBlockR\apowered\"\x9e\x01\n" +
|
||||||
"\fPoweredBlock\x12\x1a\n" +
|
"\fPoweredBlock\x12\x1a\n" +
|
||||||
"\btemplate\x18\x01 \x01(\tR\btemplate\x12\x1b\n" +
|
"\btemplate\x18\x01 \x01(\tR\btemplate\x12\x1b\n" +
|
||||||
"\tdata_json\x18\x02 \x01(\fR\bdataJson\"\x93\x01\n" +
|
"\tdata_json\x18\x02 \x01(\fR\bdataJson\x12\x1f\n" +
|
||||||
|
"\vstatus_code\x18\x03 \x01(\x05R\n" +
|
||||||
|
"statusCode\x124\n" +
|
||||||
|
"\bmetadata\x18\x04 \x01(\v2\x18.abi.v1.DocumentMetadataR\bmetadata\"\xa4\x01\n" +
|
||||||
|
"\x10DocumentMetadata\x12\x14\n" +
|
||||||
|
"\x05title\x18\x01 \x01(\tR\x05title\x12 \n" +
|
||||||
|
"\vdescription\x18\x02 \x01(\tR\vdescription\x12#\n" +
|
||||||
|
"\rcanonical_url\x18\x03 \x01(\tR\fcanonicalUrl\x12\x1b\n" +
|
||||||
|
"\timage_url\x18\x04 \x01(\tR\bimageUrl\x12\x16\n" +
|
||||||
|
"\x06robots\x18\x05 \x01(\tR\x06robots\"\x93\x01\n" +
|
||||||
"\x15RenderTemplateRequest\x12!\n" +
|
"\x15RenderTemplateRequest\x12!\n" +
|
||||||
"\ftemplate_key\x18\x01 \x01(\tR\vtemplateKey\x12\x19\n" +
|
"\ftemplate_key\x18\x01 \x01(\tR\vtemplateKey\x12\x19\n" +
|
||||||
"\bdoc_json\x18\x02 \x01(\fR\adocJson\x12<\n" +
|
"\bdoc_json\x18\x02 \x01(\fR\adocJson\x12<\n" +
|
||||||
"\x0erender_context\x18\x03 \x01(\v2\x15.abi.v1.RenderContextR\rrenderContext\"T\n" +
|
"\x0erender_context\x18\x03 \x01(\v2\x15.abi.v1.RenderContextR\rrenderContext\"T\n" +
|
||||||
"\x16RenderTemplateResponse\x124\n" +
|
"\x16RenderTemplateResponse\x124\n" +
|
||||||
"\bdocument\x18\x02 \x01(\v2\x18.abi.v1.TemplateDocumentR\bdocumentJ\x04\b\x01\x10\x02\"\xbf\x02\n" +
|
"\bdocument\x18\x02 \x01(\v2\x18.abi.v1.TemplateDocumentR\bdocumentJ\x04\b\x01\x10\x02\"\x96\x03\n" +
|
||||||
"\x10TemplateDocument\x12\x1b\n" +
|
"\x10TemplateDocument\x12\x1b\n" +
|
||||||
"\tbody_html\x18\x01 \x01(\fR\bbodyHtml\x12\x1d\n" +
|
"\tbody_html\x18\x01 \x01(\fR\bbodyHtml\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
@ -1853,7 +2016,10 @@ const file_v1_render_proto_rawDesc = "" +
|
|||||||
"\x0fhead_extra_html\x18\x04 \x01(\fR\rheadExtraHtml\x12-\n" +
|
"\x0fhead_extra_html\x18\x04 \x01(\fR\rheadExtraHtml\x12-\n" +
|
||||||
"\x13body_end_extra_html\x18\x05 \x01(\fR\x10bodyEndExtraHtml\x12F\n" +
|
"\x13body_end_extra_html\x18\x05 \x01(\fR\x10bodyEndExtraHtml\x12F\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"html_attrs\x18\x06 \x03(\v2'.abi.v1.TemplateDocument.HtmlAttrsEntryR\thtmlAttrs\x1a<\n" +
|
"html_attrs\x18\x06 \x03(\v2'.abi.v1.TemplateDocument.HtmlAttrsEntryR\thtmlAttrs\x12\x1f\n" +
|
||||||
|
"\vstatus_code\x18\a \x01(\x05R\n" +
|
||||||
|
"statusCode\x124\n" +
|
||||||
|
"\bmetadata\x18\b \x01(\v2\x18.abi.v1.DocumentMetadataR\bmetadata\x1a<\n" +
|
||||||
"\x0eHtmlAttrsEntry\x12\x10\n" +
|
"\x0eHtmlAttrsEntry\x12\x10\n" +
|
||||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x88\x01\n" +
|
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x88\x01\n" +
|
||||||
@ -1916,13 +2082,16 @@ const file_v1_render_proto_rawDesc = "" +
|
|||||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a:\n" +
|
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a:\n" +
|
||||||
"\fCookiesEntry\x12\x10\n" +
|
"\fCookiesEntry\x12\x10\n" +
|
||||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"|\n" +
|
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xbe\x01\n" +
|
||||||
"\vPageContext\x12\x0e\n" +
|
"\vPageContext\x12\x0e\n" +
|
||||||
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
|
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
|
||||||
"\x04slug\x18\x02 \x01(\tR\x04slug\x12\x14\n" +
|
"\x04slug\x18\x02 \x01(\tR\x04slug\x12\x14\n" +
|
||||||
"\x05title\x18\x03 \x01(\tR\x05title\x12\x1b\n" +
|
"\x05title\x18\x03 \x01(\tR\x05title\x12\x1b\n" +
|
||||||
"\tpost_type\x18\x04 \x01(\tR\bpostType\x12\x16\n" +
|
"\tpost_type\x18\x04 \x01(\tR\bpostType\x12\x16\n" +
|
||||||
"\x06status\x18\x05 \x01(\tR\x06status\"\xaf\x02\n" +
|
"\x06status\x18\x05 \x01(\tR\x06status\x12!\n" +
|
||||||
|
"\fderived_slug\x18\x06 \x01(\tR\vderivedSlug\x12\x1d\n" +
|
||||||
|
"\n" +
|
||||||
|
"mount_path\x18\a \x01(\tR\tmountPath\"\xaf\x02\n" +
|
||||||
"\vPostContext\x12\x0e\n" +
|
"\vPostContext\x12\x0e\n" +
|
||||||
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
|
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
|
||||||
"\x04slug\x18\x02 \x01(\tR\x04slug\x12\x14\n" +
|
"\x04slug\x18\x02 \x01(\tR\x04slug\x12\x14\n" +
|
||||||
@ -1957,7 +2126,7 @@ const file_v1_render_proto_rawDesc = "" +
|
|||||||
"\tDetailRow\x12\x19\n" +
|
"\tDetailRow\x12\x19\n" +
|
||||||
"\btable_id\x18\x01 \x01(\tR\atableId\x12\x15\n" +
|
"\btable_id\x18\x01 \x01(\tR\atableId\x12\x15\n" +
|
||||||
"\x06row_id\x18\x02 \x01(\tR\x05rowId\x12\x1b\n" +
|
"\x06row_id\x18\x02 \x01(\tR\x05rowId\x12\x1b\n" +
|
||||||
"\tdata_json\x18\x03 \x01(\fR\bdataJson\"\x85\f\n" +
|
"\tdata_json\x18\x03 \x01(\fR\bdataJson\"\xc7\f\n" +
|
||||||
"\fBlockContext\x12\x10\n" +
|
"\fBlockContext\x12\x10\n" +
|
||||||
"\x03url\x18\x01 \x01(\tR\x03url\x12\x12\n" +
|
"\x03url\x18\x01 \x01(\tR\x03url\x12\x12\n" +
|
||||||
"\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" +
|
"\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" +
|
||||||
@ -2001,7 +2170,10 @@ const file_v1_render_proto_rawDesc = "" +
|
|||||||
"\x0fdetail_table_id\x18# \x01(\tR\rdetailTableId\x12/\n" +
|
"\x0fdetail_table_id\x18# \x01(\tR\rdetailTableId\x12/\n" +
|
||||||
"\x14detail_row_data_json\x18$ \x01(\fR\x11detailRowDataJson\x12$\n" +
|
"\x14detail_row_data_json\x18$ \x01(\fR\x11detailRowDataJson\x12$\n" +
|
||||||
"\x0eblog_index_url\x18% \x01(\tR\fblogIndexUrl\x12*\n" +
|
"\x0eblog_index_url\x18% \x01(\tR\fblogIndexUrl\x12*\n" +
|
||||||
"\x11category_page_url\x18& \x01(\tR\x0fcategoryPageUrl\x1a8\n" +
|
"\x11category_page_url\x18& \x01(\tR\x0fcategoryPageUrl\x12!\n" +
|
||||||
|
"\fderived_slug\x18' \x01(\tR\vderivedSlug\x12\x1d\n" +
|
||||||
|
"\n" +
|
||||||
|
"mount_path\x18( \x01(\tR\tmountPath\x1a8\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"QueryEntry\x12\x10\n" +
|
"QueryEntry\x12\x10\n" +
|
||||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||||
@ -2025,66 +2197,69 @@ func file_v1_render_proto_rawDescGZIP() []byte {
|
|||||||
return file_v1_render_proto_rawDescData
|
return file_v1_render_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_v1_render_proto_msgTypes = make([]protoimpl.MessageInfo, 27)
|
var file_v1_render_proto_msgTypes = make([]protoimpl.MessageInfo, 28)
|
||||||
var file_v1_render_proto_goTypes = []any{
|
var file_v1_render_proto_goTypes = []any{
|
||||||
(*RenderBlockRequest)(nil), // 0: abi.v1.RenderBlockRequest
|
(*RenderBlockRequest)(nil), // 0: abi.v1.RenderBlockRequest
|
||||||
(*RenderBlockResponse)(nil), // 1: abi.v1.RenderBlockResponse
|
(*RenderBlockResponse)(nil), // 1: abi.v1.RenderBlockResponse
|
||||||
(*PoweredBlock)(nil), // 2: abi.v1.PoweredBlock
|
(*PoweredBlock)(nil), // 2: abi.v1.PoweredBlock
|
||||||
(*RenderTemplateRequest)(nil), // 3: abi.v1.RenderTemplateRequest
|
(*DocumentMetadata)(nil), // 3: abi.v1.DocumentMetadata
|
||||||
(*RenderTemplateResponse)(nil), // 4: abi.v1.RenderTemplateResponse
|
(*RenderTemplateRequest)(nil), // 4: abi.v1.RenderTemplateRequest
|
||||||
(*TemplateDocument)(nil), // 5: abi.v1.TemplateDocument
|
(*RenderTemplateResponse)(nil), // 5: abi.v1.RenderTemplateResponse
|
||||||
(*RenderTagRequest)(nil), // 6: abi.v1.RenderTagRequest
|
(*TemplateDocument)(nil), // 6: abi.v1.TemplateDocument
|
||||||
(*RenderTagResponse)(nil), // 7: abi.v1.RenderTagResponse
|
(*RenderTagRequest)(nil), // 7: abi.v1.RenderTagRequest
|
||||||
(*ApplyFilterRequest)(nil), // 8: abi.v1.ApplyFilterRequest
|
(*RenderTagResponse)(nil), // 8: abi.v1.RenderTagResponse
|
||||||
(*ApplyFilterResponse)(nil), // 9: abi.v1.ApplyFilterResponse
|
(*ApplyFilterRequest)(nil), // 9: abi.v1.ApplyFilterRequest
|
||||||
(*RenderContext)(nil), // 10: abi.v1.RenderContext
|
(*ApplyFilterResponse)(nil), // 10: abi.v1.ApplyFilterResponse
|
||||||
(*RequestInfo)(nil), // 11: abi.v1.RequestInfo
|
(*RenderContext)(nil), // 11: abi.v1.RenderContext
|
||||||
(*PageContext)(nil), // 12: abi.v1.PageContext
|
(*RequestInfo)(nil), // 12: abi.v1.RequestInfo
|
||||||
(*PostContext)(nil), // 13: abi.v1.PostContext
|
(*PageContext)(nil), // 13: abi.v1.PageContext
|
||||||
(*AuthorContext)(nil), // 14: abi.v1.AuthorContext
|
(*PostContext)(nil), // 14: abi.v1.PostContext
|
||||||
(*CategoryContext)(nil), // 15: abi.v1.CategoryContext
|
(*AuthorContext)(nil), // 15: abi.v1.AuthorContext
|
||||||
(*MasterPageContext)(nil), // 16: abi.v1.MasterPageContext
|
(*CategoryContext)(nil), // 16: abi.v1.CategoryContext
|
||||||
(*HumanProofBanner)(nil), // 17: abi.v1.HumanProofBanner
|
(*MasterPageContext)(nil), // 17: abi.v1.MasterPageContext
|
||||||
(*DetailRow)(nil), // 18: abi.v1.DetailRow
|
(*HumanProofBanner)(nil), // 18: abi.v1.HumanProofBanner
|
||||||
(*BlockContext)(nil), // 19: abi.v1.BlockContext
|
(*DetailRow)(nil), // 19: abi.v1.DetailRow
|
||||||
nil, // 20: abi.v1.TemplateDocument.HtmlAttrsEntry
|
(*BlockContext)(nil), // 20: abi.v1.BlockContext
|
||||||
nil, // 21: abi.v1.RenderContext.InjectedSlotsEntry
|
nil, // 21: abi.v1.TemplateDocument.HtmlAttrsEntry
|
||||||
nil, // 22: abi.v1.RequestInfo.HeadersEntry
|
nil, // 22: abi.v1.RenderContext.InjectedSlotsEntry
|
||||||
nil, // 23: abi.v1.RequestInfo.CookiesEntry
|
nil, // 23: abi.v1.RequestInfo.HeadersEntry
|
||||||
nil, // 24: abi.v1.BlockContext.QueryEntry
|
nil, // 24: abi.v1.RequestInfo.CookiesEntry
|
||||||
nil, // 25: abi.v1.BlockContext.CookiesEntry
|
nil, // 25: abi.v1.BlockContext.QueryEntry
|
||||||
nil, // 26: abi.v1.BlockContext.HeadersEntry
|
nil, // 26: abi.v1.BlockContext.CookiesEntry
|
||||||
(*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp
|
nil, // 27: abi.v1.BlockContext.HeadersEntry
|
||||||
|
(*timestamppb.Timestamp)(nil), // 28: google.protobuf.Timestamp
|
||||||
}
|
}
|
||||||
var file_v1_render_proto_depIdxs = []int32{
|
var file_v1_render_proto_depIdxs = []int32{
|
||||||
10, // 0: abi.v1.RenderBlockRequest.render_context:type_name -> abi.v1.RenderContext
|
11, // 0: abi.v1.RenderBlockRequest.render_context:type_name -> abi.v1.RenderContext
|
||||||
2, // 1: abi.v1.RenderBlockResponse.powered:type_name -> abi.v1.PoweredBlock
|
2, // 1: abi.v1.RenderBlockResponse.powered:type_name -> abi.v1.PoweredBlock
|
||||||
10, // 2: abi.v1.RenderTemplateRequest.render_context:type_name -> abi.v1.RenderContext
|
3, // 2: abi.v1.PoweredBlock.metadata:type_name -> abi.v1.DocumentMetadata
|
||||||
5, // 3: abi.v1.RenderTemplateResponse.document:type_name -> abi.v1.TemplateDocument
|
11, // 3: abi.v1.RenderTemplateRequest.render_context:type_name -> abi.v1.RenderContext
|
||||||
20, // 4: abi.v1.TemplateDocument.html_attrs:type_name -> abi.v1.TemplateDocument.HtmlAttrsEntry
|
6, // 4: abi.v1.RenderTemplateResponse.document:type_name -> abi.v1.TemplateDocument
|
||||||
10, // 5: abi.v1.RenderTagRequest.render_context:type_name -> abi.v1.RenderContext
|
21, // 5: abi.v1.TemplateDocument.html_attrs:type_name -> abi.v1.TemplateDocument.HtmlAttrsEntry
|
||||||
11, // 6: abi.v1.RenderContext.request:type_name -> abi.v1.RequestInfo
|
3, // 6: abi.v1.TemplateDocument.metadata:type_name -> abi.v1.DocumentMetadata
|
||||||
19, // 7: abi.v1.RenderContext.block_context:type_name -> abi.v1.BlockContext
|
11, // 7: abi.v1.RenderTagRequest.render_context:type_name -> abi.v1.RenderContext
|
||||||
12, // 8: abi.v1.RenderContext.page:type_name -> abi.v1.PageContext
|
12, // 8: abi.v1.RenderContext.request:type_name -> abi.v1.RequestInfo
|
||||||
13, // 9: abi.v1.RenderContext.post:type_name -> abi.v1.PostContext
|
20, // 9: abi.v1.RenderContext.block_context:type_name -> abi.v1.BlockContext
|
||||||
14, // 10: abi.v1.RenderContext.author:type_name -> abi.v1.AuthorContext
|
13, // 10: abi.v1.RenderContext.page:type_name -> abi.v1.PageContext
|
||||||
15, // 11: abi.v1.RenderContext.category:type_name -> abi.v1.CategoryContext
|
14, // 11: abi.v1.RenderContext.post:type_name -> abi.v1.PostContext
|
||||||
16, // 12: abi.v1.RenderContext.master_page:type_name -> abi.v1.MasterPageContext
|
15, // 12: abi.v1.RenderContext.author:type_name -> abi.v1.AuthorContext
|
||||||
21, // 13: abi.v1.RenderContext.injected_slots:type_name -> abi.v1.RenderContext.InjectedSlotsEntry
|
16, // 13: abi.v1.RenderContext.category:type_name -> abi.v1.CategoryContext
|
||||||
17, // 14: abi.v1.RenderContext.human_proof_banner:type_name -> abi.v1.HumanProofBanner
|
17, // 14: abi.v1.RenderContext.master_page:type_name -> abi.v1.MasterPageContext
|
||||||
18, // 15: abi.v1.RenderContext.detail_row:type_name -> abi.v1.DetailRow
|
22, // 15: abi.v1.RenderContext.injected_slots:type_name -> abi.v1.RenderContext.InjectedSlotsEntry
|
||||||
22, // 16: abi.v1.RequestInfo.headers:type_name -> abi.v1.RequestInfo.HeadersEntry
|
18, // 16: abi.v1.RenderContext.human_proof_banner:type_name -> abi.v1.HumanProofBanner
|
||||||
23, // 17: abi.v1.RequestInfo.cookies:type_name -> abi.v1.RequestInfo.CookiesEntry
|
19, // 17: abi.v1.RenderContext.detail_row:type_name -> abi.v1.DetailRow
|
||||||
27, // 18: abi.v1.PostContext.published_at:type_name -> google.protobuf.Timestamp
|
23, // 18: abi.v1.RequestInfo.headers:type_name -> abi.v1.RequestInfo.HeadersEntry
|
||||||
27, // 19: abi.v1.BlockContext.now:type_name -> google.protobuf.Timestamp
|
24, // 19: abi.v1.RequestInfo.cookies:type_name -> abi.v1.RequestInfo.CookiesEntry
|
||||||
24, // 20: abi.v1.BlockContext.query:type_name -> abi.v1.BlockContext.QueryEntry
|
28, // 20: abi.v1.PostContext.published_at:type_name -> google.protobuf.Timestamp
|
||||||
25, // 21: abi.v1.BlockContext.cookies:type_name -> abi.v1.BlockContext.CookiesEntry
|
28, // 21: abi.v1.BlockContext.now:type_name -> google.protobuf.Timestamp
|
||||||
26, // 22: abi.v1.BlockContext.headers:type_name -> abi.v1.BlockContext.HeadersEntry
|
25, // 22: abi.v1.BlockContext.query:type_name -> abi.v1.BlockContext.QueryEntry
|
||||||
23, // [23:23] is the sub-list for method output_type
|
26, // 23: abi.v1.BlockContext.cookies:type_name -> abi.v1.BlockContext.CookiesEntry
|
||||||
23, // [23:23] is the sub-list for method input_type
|
27, // 24: abi.v1.BlockContext.headers:type_name -> abi.v1.BlockContext.HeadersEntry
|
||||||
23, // [23:23] is the sub-list for extension type_name
|
25, // [25:25] is the sub-list for method output_type
|
||||||
23, // [23:23] is the sub-list for extension extendee
|
25, // [25:25] is the sub-list for method input_type
|
||||||
0, // [0:23] is the sub-list for field type_name
|
25, // [25:25] is the sub-list for extension type_name
|
||||||
|
25, // [25:25] is the sub-list for extension extendee
|
||||||
|
0, // [0:25] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_v1_render_proto_init() }
|
func init() { file_v1_render_proto_init() }
|
||||||
@ -2098,7 +2273,7 @@ func file_v1_render_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1_render_proto_rawDesc), len(file_v1_render_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1_render_proto_rawDesc), len(file_v1_render_proto_rawDesc)),
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 27,
|
NumMessages: 28,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -97,10 +98,8 @@ func TestCaptchaVerifiedReadsTrustedHeader(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAllTrustedHeadersIncludesCaptcha(t *testing.T) {
|
func TestAllTrustedHeadersIncludesCaptcha(t *testing.T) {
|
||||||
for _, h := range AllTrustedHeaders() {
|
if slices.Contains(AllTrustedHeaders(), HeaderVerifiedCaptcha) {
|
||||||
if h == HeaderVerifiedCaptcha {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
t.Fatalf("AllTrustedHeaders() missing %s — hosts strip-then-stamp from this list, so omitting it makes the header client-forgeable", HeaderVerifiedCaptcha)
|
t.Fatalf("AllTrustedHeaders() missing %s — hosts strip-then-stamp from this list, so omitting it makes the header client-forgeable", HeaderVerifiedCaptcha)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,11 +13,13 @@ import (
|
|||||||
// PageContext provides page information to blocks via context.
|
// PageContext provides page information to blocks via context.
|
||||||
// The CMS populates this from its internal db.Page type.
|
// The CMS populates this from its internal db.Page type.
|
||||||
type PageContext struct {
|
type PageContext struct {
|
||||||
ID uuid.UUID
|
ID uuid.UUID
|
||||||
Slug string
|
Slug string
|
||||||
Title string
|
DerivedSlug string // resolved route from the page hierarchy
|
||||||
PostType string // "page", "post", "master", "system"
|
MountPath string // effective host-resolved mount prefix
|
||||||
Status string // "published", "draft", "scheduled"
|
Title string
|
||||||
|
PostType string // "page", "post", "master", "system"
|
||||||
|
Status string // "published", "draft", "scheduled"
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostContext provides blog post information to blocks via context.
|
// PostContext provides blog post information to blocks via context.
|
||||||
@ -368,44 +370,46 @@ func RenderSlot(ctx context.Context) string {
|
|||||||
|
|
||||||
// BlockContext provides contextual information available to all block templates.
|
// BlockContext provides contextual information available to all block templates.
|
||||||
type BlockContext struct {
|
type BlockContext struct {
|
||||||
URL string `pongo2:"url"`
|
URL string `pongo2:"url"`
|
||||||
Path string `pongo2:"path"`
|
Path string `pongo2:"path"`
|
||||||
Slug string `pongo2:"slug"`
|
Slug string `pongo2:"slug"`
|
||||||
PageID string `pongo2:"pageId"`
|
DerivedSlug string `pongo2:"derivedSlug"`
|
||||||
PageTitle string `pongo2:"pageTitle"`
|
MountPath string `pongo2:"mountPath"`
|
||||||
TemplateKey string `pongo2:"templateKey"`
|
PageID string `pongo2:"pageId"`
|
||||||
IsEditor bool `pongo2:"isEditor"`
|
PageTitle string `pongo2:"pageTitle"`
|
||||||
Timestamp int64 `pongo2:"timestamp"`
|
TemplateKey string `pongo2:"templateKey"`
|
||||||
IsLoggedIn bool `pongo2:"isLoggedIn"`
|
IsEditor bool `pongo2:"isEditor"`
|
||||||
UserID string `pongo2:"userId"`
|
Timestamp int64 `pongo2:"timestamp"`
|
||||||
UserEmail string `pongo2:"userEmail"`
|
IsLoggedIn bool `pongo2:"isLoggedIn"`
|
||||||
UserRole string `pongo2:"userRole"`
|
UserID string `pongo2:"userId"`
|
||||||
Method string `pongo2:"method"`
|
UserEmail string `pongo2:"userEmail"`
|
||||||
Host string `pongo2:"host"`
|
UserRole string `pongo2:"userRole"`
|
||||||
Query map[string]string `pongo2:"query"`
|
Method string `pongo2:"method"`
|
||||||
Referrer string `pongo2:"referrer"`
|
Host string `pongo2:"host"`
|
||||||
UserAgent string `pongo2:"userAgent"`
|
Query map[string]string `pongo2:"query"`
|
||||||
IP string `pongo2:"ip"`
|
Referrer string `pongo2:"referrer"`
|
||||||
Cookies map[string]string `pongo2:"cookies"`
|
UserAgent string `pongo2:"userAgent"`
|
||||||
Headers map[string]string `pongo2:"headers"`
|
IP string `pongo2:"ip"`
|
||||||
Country string `pongo2:"country"`
|
Cookies map[string]string `pongo2:"cookies"`
|
||||||
City string `pongo2:"city"`
|
Headers map[string]string `pongo2:"headers"`
|
||||||
Timezone string `pongo2:"timezone"`
|
Country string `pongo2:"country"`
|
||||||
Now time.Time `pongo2:"now"`
|
City string `pongo2:"city"`
|
||||||
CurrentAuthor map[string]any `pongo2:"currentAuthor"`
|
Timezone string `pongo2:"timezone"`
|
||||||
CurrentPost map[string]any `pongo2:"currentPost"`
|
Now time.Time `pongo2:"now"`
|
||||||
CurrentCategory map[string]any `pongo2:"currentCategory"`
|
CurrentAuthor map[string]any `pongo2:"currentAuthor"`
|
||||||
Site map[string]any `pongo2:"site"`
|
CurrentPost map[string]any `pongo2:"currentPost"`
|
||||||
IsPublicLoggedIn bool `pongo2:"isPublicLoggedIn"`
|
CurrentCategory map[string]any `pongo2:"currentCategory"`
|
||||||
PublicUserID string `pongo2:"publicUserId"`
|
Site map[string]any `pongo2:"site"`
|
||||||
PublicUsername string `pongo2:"publicUsername"`
|
IsPublicLoggedIn bool `pongo2:"isPublicLoggedIn"`
|
||||||
PublicDisplayName string `pongo2:"publicDisplayName"`
|
PublicUserID string `pongo2:"publicUserId"`
|
||||||
PublicEmailVerified bool `pongo2:"publicEmailVerified"`
|
PublicUsername string `pongo2:"publicUsername"`
|
||||||
DetailRowID string `pongo2:"detailRowId"`
|
PublicDisplayName string `pongo2:"publicDisplayName"`
|
||||||
DetailTableID string `pongo2:"detailTableId"`
|
PublicEmailVerified bool `pongo2:"publicEmailVerified"`
|
||||||
DetailRowData map[string]any `pongo2:"detailRowData"`
|
DetailRowID string `pongo2:"detailRowId"`
|
||||||
BlogIndexURL string `pongo2:"blogIndexUrl"`
|
DetailTableID string `pongo2:"detailTableId"`
|
||||||
CategoryPageURL string `pongo2:"categoryPageUrl"`
|
DetailRowData map[string]any `pongo2:"detailRowData"`
|
||||||
|
BlogIndexURL string `pongo2:"blogIndexUrl"`
|
||||||
|
CategoryPageURL string `pongo2:"categoryPageUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type blockContextKey struct{}
|
type blockContextKey struct{}
|
||||||
@ -425,6 +429,7 @@ func GetBlockContext(ctx context.Context) *BlockContext {
|
|||||||
func (bc *BlockContext) ToMap() map[string]any {
|
func (bc *BlockContext) ToMap() map[string]any {
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
"url": bc.URL, "path": bc.Path, "slug": bc.Slug,
|
"url": bc.URL, "path": bc.Path, "slug": bc.Slug,
|
||||||
|
"derivedSlug": bc.DerivedSlug, "mountPath": bc.MountPath,
|
||||||
"pageId": bc.PageID, "pageTitle": bc.PageTitle,
|
"pageId": bc.PageID, "pageTitle": bc.PageTitle,
|
||||||
"templateKey": bc.TemplateKey, "isEditor": bc.IsEditor,
|
"templateKey": bc.TemplateKey, "isEditor": bc.IsEditor,
|
||||||
"timestamp": bc.Timestamp, "now": bc.Now,
|
"timestamp": bc.Timestamp, "now": bc.Now,
|
||||||
@ -437,12 +442,12 @@ func (bc *BlockContext) ToMap() map[string]any {
|
|||||||
"currentAuthor": bc.CurrentAuthor, "currentPost": bc.CurrentPost,
|
"currentAuthor": bc.CurrentAuthor, "currentPost": bc.CurrentPost,
|
||||||
"currentCategory": bc.CurrentCategory, "site": bc.Site,
|
"currentCategory": bc.CurrentCategory, "site": bc.Site,
|
||||||
"isPublicLoggedIn": bc.IsPublicLoggedIn,
|
"isPublicLoggedIn": bc.IsPublicLoggedIn,
|
||||||
"publicUserId": bc.PublicUserID, "publicUsername": bc.PublicUsername,
|
"publicUserId": bc.PublicUserID, "publicUsername": bc.PublicUsername,
|
||||||
"publicDisplayName": bc.PublicDisplayName,
|
"publicDisplayName": bc.PublicDisplayName,
|
||||||
"publicEmailVerified": bc.PublicEmailVerified,
|
"publicEmailVerified": bc.PublicEmailVerified,
|
||||||
"detailRowId": bc.DetailRowID, "detailTableId": bc.DetailTableID,
|
"detailRowId": bc.DetailRowID, "detailTableId": bc.DetailTableID,
|
||||||
"detailRowData": bc.DetailRowData,
|
"detailRowData": bc.DetailRowData,
|
||||||
"blogIndexUrl": bc.BlogIndexURL, "categoryPageUrl": bc.CategoryPageURL,
|
"blogIndexUrl": bc.BlogIndexURL, "categoryPageUrl": bc.CategoryPageURL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package blocks
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/pluginsdk/document"
|
||||||
)
|
)
|
||||||
|
|
||||||
// poweredBlockSentinel prefixes a PoweredBlock marker string. It uses NUL
|
// poweredBlockSentinel prefixes a PoweredBlock marker string. It uses NUL
|
||||||
@ -16,8 +18,17 @@ const poweredBlockSentinel = "\x00bn:powered\x00"
|
|||||||
// and forwards it as abiv1.PoweredBlock so the HOST renders the template with
|
// and forwards it as abiv1.PoweredBlock so the HOST renders the template with
|
||||||
// pongo2 — after the block-invoke has returned, keeping the guest free.
|
// pongo2 — after the block-invoke has returned, keeping the guest free.
|
||||||
type PoweredResult struct {
|
type PoweredResult struct {
|
||||||
Template string `json:"template"`
|
Template string `json:"template"`
|
||||||
Data map[string]any `json:"data"`
|
Data map[string]any `json:"data"`
|
||||||
|
StatusCode int32 `json:"statusCode,omitempty"`
|
||||||
|
Metadata *document.Metadata `json:"metadata,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PoweredOptions controls host-owned response data for a powered block.
|
||||||
|
// StatusCode is transported as declared; the host accepts only 200–599.
|
||||||
|
type PoweredOptions struct {
|
||||||
|
StatusCode int32
|
||||||
|
Metadata *document.Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// PoweredBlock marks a block's return value as "powered": instead of final
|
// PoweredBlock marks a block's return value as "powered": instead of final
|
||||||
@ -37,11 +48,28 @@ type PoweredResult struct {
|
|||||||
// hits ({% mytag %} / |myfilter) is a fresh RENDER_TAG / APPLY_FILTER invoke,
|
// hits ({% mytag %} / |myfilter) is a fresh RENDER_TAG / APPLY_FILTER invoke,
|
||||||
// never a re-entrant one.
|
// never a re-entrant one.
|
||||||
func PoweredBlock(template string, data map[string]any) string {
|
func PoweredBlock(template string, data map[string]any) string {
|
||||||
payload, err := json.Marshal(PoweredResult{Template: template, Data: data})
|
return poweredBlockMarker(PoweredResult{Template: template, Data: data})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PoweredBlockWithOptions is PoweredBlock with structured document metadata
|
||||||
|
// and an optional HTTP response status for the host to accumulate before it
|
||||||
|
// writes response headers.
|
||||||
|
func PoweredBlockWithOptions(template string, data map[string]any, options PoweredOptions) string {
|
||||||
|
return poweredBlockMarker(PoweredResult{
|
||||||
|
Template: template,
|
||||||
|
Data: data,
|
||||||
|
StatusCode: options.StatusCode,
|
||||||
|
Metadata: options.Metadata,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func poweredBlockMarker(result PoweredResult) string {
|
||||||
|
payload, err := json.Marshal(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// A non-serializable data map is a programming error; fall back to an
|
// A non-serializable data map is a programming error; fall back to an
|
||||||
// empty-data powered result so the template still renders.
|
// empty-data result while preserving response options.
|
||||||
payload, _ = json.Marshal(PoweredResult{Template: template})
|
result.Data = nil
|
||||||
|
payload, _ = json.Marshal(result)
|
||||||
}
|
}
|
||||||
return poweredBlockSentinel + string(payload)
|
return poweredBlockSentinel + string(payload)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
package blocks
|
package blocks
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/pluginsdk/document"
|
||||||
|
)
|
||||||
|
|
||||||
func TestPoweredBlockRoundTrip(t *testing.T) {
|
func TestPoweredBlockRoundTrip(t *testing.T) {
|
||||||
marker := PoweredBlock("<p>{{ x }}</p>", map[string]any{"x": "y"})
|
marker := PoweredBlock("<p>{{ x }}</p>", map[string]any{"x": "y"})
|
||||||
@ -16,6 +20,47 @@ func TestPoweredBlockRoundTrip(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPoweredBlockWithOptionsRoundTrip(t *testing.T) {
|
||||||
|
metadata := &document.Metadata{
|
||||||
|
Title: "Missing article",
|
||||||
|
Description: "The requested article was not found.",
|
||||||
|
CanonicalURL: "https://example.com/docs/missing",
|
||||||
|
ImageURL: "https://example.com/media/missing.png",
|
||||||
|
Robots: "noindex,nofollow",
|
||||||
|
}
|
||||||
|
marker := PoweredBlockWithOptions("<h1>{{ title }}</h1>", map[string]any{"title": "Missing"}, PoweredOptions{
|
||||||
|
StatusCode: 404,
|
||||||
|
Metadata: metadata,
|
||||||
|
})
|
||||||
|
got, ok := DecodePoweredBlock(marker)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("DecodePoweredBlock did not recognize its own marker")
|
||||||
|
}
|
||||||
|
if got.StatusCode != 404 {
|
||||||
|
t.Errorf("status code = %d, want 404", got.StatusCode)
|
||||||
|
}
|
||||||
|
if got.Metadata == nil || *got.Metadata != *metadata {
|
||||||
|
t.Errorf("metadata = %+v, want %+v", got.Metadata, metadata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPoweredBlockWithOptionsPreservesOptionsWhenDataCannotMarshal(t *testing.T) {
|
||||||
|
marker := PoweredBlockWithOptions("<h1>fallback</h1>", map[string]any{"bad": make(chan int)}, PoweredOptions{
|
||||||
|
StatusCode: 503,
|
||||||
|
Metadata: &document.Metadata{Title: "Unavailable"},
|
||||||
|
})
|
||||||
|
got, ok := DecodePoweredBlock(marker)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("DecodePoweredBlock did not recognize fallback marker")
|
||||||
|
}
|
||||||
|
if got.Data != nil {
|
||||||
|
t.Errorf("data = %#v, want nil fallback", got.Data)
|
||||||
|
}
|
||||||
|
if got.StatusCode != 503 || got.Metadata == nil || got.Metadata.Title != "Unavailable" {
|
||||||
|
t.Errorf("options were not preserved: %+v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDecodePoweredBlockRejectsPlainHTML(t *testing.T) {
|
func TestDecodePoweredBlockRejectsPlainHTML(t *testing.T) {
|
||||||
if _, ok := DecodePoweredBlock("<p>hello</p>"); ok {
|
if _, ok := DecodePoweredBlock("<p>hello</p>"); ok {
|
||||||
t.Error("plain HTML must not decode as a powered block")
|
t.Error("plain HTML must not decode as a powered block")
|
||||||
|
|||||||
@ -0,0 +1,40 @@
|
|||||||
|
# Keyed load-once hooks have durable host receipts
|
||||||
|
|
||||||
|
Plugin `Load` callbacks are deliberately repeatable: startup, enable, update,
|
||||||
|
and provisioning-consent replay can all invoke them. Some initialization is
|
||||||
|
different. A plugin may add a suggested menu link on its first load, but must
|
||||||
|
not restore that link after an administrator later removes or renames it.
|
||||||
|
|
||||||
|
Decision: `PluginRegistration.LoadOnce` is an ordered slice of callbacks with
|
||||||
|
stable keys. DESCRIBE publishes those keys in `PluginManifest.load_once_keys`.
|
||||||
|
The host checks a durable `(plugin, key)` receipt before dispatching
|
||||||
|
`HOOK_LOAD_ONCE`; it records the receipt only after success. A completed key is
|
||||||
|
not removed when the plugin is disabled, updated, or uninstalled. A later
|
||||||
|
release that intentionally needs another one-time action declares a new key.
|
||||||
|
|
||||||
|
Load-once runs after the ordinary `Load` hook so repeatable provisioning can
|
||||||
|
create resources that a one-time callback references. Keys are lowercase,
|
||||||
|
MCP-style stable identifiers of at most 128 bytes and must be unique within a
|
||||||
|
registration. Failed callbacks may be retried because no completion receipt
|
||||||
|
exists. As with database migrations, callbacks should still make their own
|
||||||
|
external mutation idempotent to cover a process failure between the mutation
|
||||||
|
and receipt commit.
|
||||||
|
|
||||||
|
Encoding first-install booleans in plugin settings was rejected because it
|
||||||
|
mixes host lifecycle state with customer-visible configuration. Making all
|
||||||
|
provisioner operations one-shot was rejected because pages, settings defaults,
|
||||||
|
and other declarations intentionally reconcile on every load. Version-based
|
||||||
|
execution was rejected because an unrelated release must not repeat the side
|
||||||
|
effect.
|
||||||
|
|
||||||
|
Consequences:
|
||||||
|
|
||||||
|
- Native and WASM plugins share one keyed lifecycle abstraction.
|
||||||
|
- The host, not guest memory, owns durable completion state.
|
||||||
|
- Updating or re-enabling a plugin cannot recreate an administrator-removed
|
||||||
|
resource under an already completed key.
|
||||||
|
- Changing a key is an explicit request to run a new callback once.
|
||||||
|
|
||||||
|
Keywords: PluginRegistration.LoadOnce, LoadOnceHook, load_once_keys,
|
||||||
|
HOOK_LOAD_ONCE, durable receipt, first load, plugin update, idempotency,
|
||||||
|
one-time provisioning, lifecycle
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
# Provisioned pages use stable keys and render results carry safe response data
|
||||||
|
|
||||||
|
Provisioned pages were identified only by slug. Slugs are customer-facing
|
||||||
|
content and can change when an administrator renames a page, changes its
|
||||||
|
hierarchy, or mounts a plugin below a customer-specific route. A later plugin
|
||||||
|
load could therefore fail to find its page, recreate it, or attach children
|
||||||
|
and menu items to a stale location.
|
||||||
|
|
||||||
|
Decision: a page declaration may carry a plugin-scoped `Key`. `ParentKey`
|
||||||
|
references another declaration from the same plugin, while menu items use the
|
||||||
|
pair `PagePlugin` and `PageKey` so they can target a provisioned page across
|
||||||
|
plugin boundaries. The existing `ParentSlug` field remains an explicit legacy
|
||||||
|
input, but keyed references are the stable identity for new declarations.
|
||||||
|
|
||||||
|
`MountPath` is create-only. The host applies it while creating the page but
|
||||||
|
does not relocate an existing customer page during later reconciliation.
|
||||||
|
`MergeMissingBlockContent` enables a shallow default merge for matching
|
||||||
|
plugin-owned block assignments: only absent top-level keys are added, and
|
||||||
|
present values such as an empty string, zero, false, or null are preserved.
|
||||||
|
|
||||||
|
The render ABI exposes both `DerivedSlug` and `MountPath` on page and block
|
||||||
|
contexts. `DerivedSlug` is computed from the resolved hierarchy. `MountPath`
|
||||||
|
is the effective, normalized value after host inheritance, rather than the raw
|
||||||
|
stored declaration. Guests consume these values and do not reimplement host
|
||||||
|
routing rules.
|
||||||
|
|
||||||
|
Powered blocks and template documents may also request an HTTP status and a
|
||||||
|
shared `DocumentMetadata` value. Metadata is deliberately limited to title,
|
||||||
|
description, canonical URL, image URL, and robots directives. The host
|
||||||
|
validates status codes and metadata, escapes values, and accumulates status
|
||||||
|
before writing headers. Only 200–599 is accepted; the highest status class
|
||||||
|
wins, with the first result retained within a class. For metadata, the first
|
||||||
|
non-empty value for each field wins in deterministic render order, after which
|
||||||
|
page/document defaults fill gaps. Arbitrary meta/link maps were rejected
|
||||||
|
because they would create a second unvalidated head-injection path; advanced
|
||||||
|
trusted templates already have the explicit `HeadExtra` surface.
|
||||||
|
|
||||||
|
Appending fields to the v1 protobuf messages was chosen over renumbering or a
|
||||||
|
Go-only helper because `abi/proto/v1` is the language-neutral contract. Slug
|
||||||
|
fallbacks remain data in that contract; the SDK does not silently synthesize
|
||||||
|
keys from mutable slugs.
|
||||||
|
|
||||||
|
Consequences:
|
||||||
|
|
||||||
|
- Plugin updates can find the same provisioned page after customer route
|
||||||
|
changes without taking ownership of the customer's chosen slug.
|
||||||
|
- Parent and menu references survive page moves and renames.
|
||||||
|
- Changing a declaration's mount path does not unexpectedly move a live page.
|
||||||
|
- Plugins can add newly introduced block defaults without overwriting customer
|
||||||
|
configuration.
|
||||||
|
- Native and WASM render code receive the same host-resolved routing values.
|
||||||
|
- Wiki-like powered blocks can return correct not-found status and structured
|
||||||
|
metadata without rendering or injecting the document envelope themselves.
|
||||||
|
|
||||||
|
Keywords: PageConfig.Key, ParentKey, ParentSlug, MountPath,
|
||||||
|
MergeMissingBlockContent, MenuItemConfig.PagePlugin, PageKey, DerivedSlug,
|
||||||
|
PageSeed, RenderContext, PoweredBlockWithOptions, DocumentMetadata,
|
||||||
|
TemplateDocument, stable identity, page provisioning
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
# Plugin manifests declare admin API compatibility
|
||||||
|
|
||||||
|
Plugin admin bundles consume the versioned `@block-ninja/api` browser package.
|
||||||
|
The existing `block_core` compatibility constraint describes the host plugin
|
||||||
|
runtime, but it cannot tell a registry or CMS whether an admin bundle is safe
|
||||||
|
to load against the host's browser API. Treating those surfaces as one version
|
||||||
|
would couple independent release cycles and allow an otherwise compatible
|
||||||
|
plugin to fail only after its admin page loads.
|
||||||
|
|
||||||
|
Decision: `[compatibility]` gains an optional `admin_api` string. Its value is
|
||||||
|
a semantic-version constraint for the host-provided browser admin API, for
|
||||||
|
example `admin_api = ">=0.1.2"`. The manifest/parser layer preserves the value
|
||||||
|
verbatim; registry and host resolvers own constraint validation and matching.
|
||||||
|
|
||||||
|
An omitted or empty value means the plugin has not declared browser API
|
||||||
|
compatibility. It is not equivalent to an unconstrained wildcard. Automated
|
||||||
|
latest-compatible resolution that filters by a host admin API version must
|
||||||
|
therefore fail closed for an undeclared value. This does not change the
|
||||||
|
separate `block_core` constraint or imply that every plugin has an admin
|
||||||
|
bundle.
|
||||||
|
|
||||||
|
The field is part of the shared manifest model and the CLI's hand-written
|
||||||
|
`plugin.mod` serializer so version bumps, tag edits, and initialization cannot
|
||||||
|
silently discard it.
|
||||||
|
|
||||||
|
Consequences:
|
||||||
|
|
||||||
|
- Registries and hosts can select plugin releases compatible with both the
|
||||||
|
runtime ABI and browser admin API.
|
||||||
|
- Headless plugins can continue to omit `admin_api`.
|
||||||
|
- Plugin authors express a range rather than pinning a single browser package
|
||||||
|
version.
|
||||||
|
- Constraint syntax and matching remain the resolver's responsibility rather
|
||||||
|
than being duplicated in manifest parsers.
|
||||||
|
|
||||||
|
Keywords: plugin.mod, compatibility, admin_api, AdminAPI, @block-ninja/api,
|
||||||
|
semantic version, browser API, fail closed, plugin resolver
|
||||||
27
docs/adr/0005-plugin-manifests-declare-sdk-compatibility.md
Normal file
27
docs/adr/0005-plugin-manifests-declare-sdk-compatibility.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Plugin manifests declare Plugin SDK compatibility
|
||||||
|
|
||||||
|
The historical `block_core` compatibility field predates the standalone
|
||||||
|
Plugin SDK and is ambiguous: CMS core and the plugin-facing SDK have separate
|
||||||
|
release cycles. Interpreting the same constraint against both versions can
|
||||||
|
select an artifact that the host cannot load or reject one that is compatible.
|
||||||
|
|
||||||
|
Decision: `[compatibility]` gains an optional `plugin_sdk` string containing a
|
||||||
|
semantic-version constraint for the plugin-facing SDK/ABI, for example
|
||||||
|
`plugin_sdk = ">=0.3.7"`. Manifest parsing preserves the value verbatim;
|
||||||
|
registry and host resolvers own constraint validation and matching.
|
||||||
|
|
||||||
|
An omitted or empty value is undeclared compatibility, not a wildcard.
|
||||||
|
Automated resolution against a host SDK version must fail closed, except for
|
||||||
|
an explicitly bounded migration policy for releases published before this
|
||||||
|
field existed. `block_core` remains a separate historical constraint and is
|
||||||
|
not reinterpreted as the CMS application's own version.
|
||||||
|
|
||||||
|
Consequences:
|
||||||
|
|
||||||
|
- Plugins can state the actual SDK/ABI range they require.
|
||||||
|
- CMS core can evolve independently of the public Plugin SDK.
|
||||||
|
- New releases must declare `plugin_sdk` to participate in automatic
|
||||||
|
SDK-compatible selection.
|
||||||
|
|
||||||
|
Keywords: plugin.mod, compatibility, plugin_sdk, PluginSDK, ABI, semantic
|
||||||
|
version, plugin resolver, fail closed
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
# Bridge providers receive authenticated caller identity
|
||||||
|
|
||||||
|
Bridge payloads are guest-controlled. A provider that uses a payload field as
|
||||||
|
an ownership namespace lets one plugin impersonate another plugin and mutate
|
||||||
|
or claim its managed records. The consumer-to-host `BridgeInvokeRequest`
|
||||||
|
cannot safely carry identity because the consumer constructs that message.
|
||||||
|
|
||||||
|
Decision: the host derives the caller plugin from the loaded module or native
|
||||||
|
plugin registration. It passes that identity to the provider in
|
||||||
|
`BridgeCallRequest.caller_plugin` and, for Go providers, through
|
||||||
|
`plugin.WithBridgeCallerPlugin`. Providers read it with
|
||||||
|
`plugin.BridgeCallerPlugin` and fail closed when an ownership-sensitive call
|
||||||
|
has no authenticated caller.
|
||||||
|
|
||||||
|
The new host-to-provider protobuf field is additive. Older providers ignore
|
||||||
|
it. New ownership-sensitive providers intentionally reject calls from older
|
||||||
|
hosts that cannot authenticate a caller. Hosts must never copy a caller name
|
||||||
|
from opaque bridge payloads or a consumer-authored capability field.
|
||||||
|
|
||||||
|
Consequences:
|
||||||
|
|
||||||
|
- Providers can derive durable ownership from an authenticated principal.
|
||||||
|
- Bridge payload schemas do not need security-sensitive source fields.
|
||||||
|
- Native and Wasm providers observe the same caller context contract.
|
||||||
|
|
||||||
|
Keywords: plugin bridge, caller identity, authentication, ownership,
|
||||||
|
BridgeCallRequest, WithBridgeCallerPlugin, Wasm, confused deputy
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
# Register sees the current guest host services
|
||||||
|
|
||||||
|
Go guest plugins can declare capability-backed runtime surfaces from their
|
||||||
|
`Register` callback. Bridge providers are the first such surface: the provider
|
||||||
|
stores its invokable value in the guest-local bridge stub while the stub tells
|
||||||
|
the CMS host that the named service exists.
|
||||||
|
|
||||||
|
`Serve` previously assigned the package-level guest runtime only after
|
||||||
|
`newGuest` returned. Because `newGuest` runs `Register` synchronously,
|
||||||
|
`HostServices()` returned an empty `CoreServices` value during registration.
|
||||||
|
Plugins that correctly nil-checked the bridge silently skipped registration.
|
||||||
|
The CMS could therefore mark a Wasm plugin loaded while consumers failed with
|
||||||
|
`bridge: no service registered` during a later load hook.
|
||||||
|
|
||||||
|
`newGuest` now makes the guest under construction current for the synchronous
|
||||||
|
registration pass and restores the previous runtime before returning. `Serve`
|
||||||
|
then installs the completed guest as before. This keeps construction isolated
|
||||||
|
for native tests while making the documented `HostServices()` escape hatch
|
||||||
|
truthful during `Register` on every pooled Wasm instance.
|
||||||
|
|
||||||
|
Alternatives rejected were moving bridge registration into `Load`, which runs
|
||||||
|
on only one pooled instance, and adding bridge names only to the manifest,
|
||||||
|
which would advertise host availability without installing the guest-local
|
||||||
|
`BridgeInvokable` value needed by `HOOK_BRIDGE_CALL`.
|
||||||
|
|
||||||
|
Consequences:
|
||||||
|
|
||||||
|
- Bridge services registered from `Register` exist on every pooled guest
|
||||||
|
instance and remain callable after CMS startup and hot swap.
|
||||||
|
- A regression test exercises the observable `Serve` plus
|
||||||
|
`HOOK_BRIDGE_CALL` contract instead of relying on initialization internals.
|
||||||
|
- No ABI or protobuf change is required; consumers need a plugin SDK release
|
||||||
|
containing the corrected Go guest runtime.
|
||||||
|
|
||||||
|
Keywords: wasmguest.Serve, newGuest, HostServices, PluginRegistration.Register,
|
||||||
|
plugin bridge, RegisterService, BridgeInvokable, HOOK_BRIDGE_CALL, wiki content
|
||||||
14
document/metadata.go
Normal file
14
document/metadata.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Package document defines structured page-response metadata shared by block
|
||||||
|
// and template render results.
|
||||||
|
package document
|
||||||
|
|
||||||
|
// Metadata is the safe document-head subset a plugin may ask the host to
|
||||||
|
// render. The host remains responsible for validating URLs and robots values
|
||||||
|
// and for escaping every value before it is written into HTML.
|
||||||
|
type Metadata struct {
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
CanonicalURL string `json:"canonicalUrl,omitempty"`
|
||||||
|
ImageURL string `json:"imageUrl,omitempty"`
|
||||||
|
Robots string `json:"robots,omitempty"`
|
||||||
|
}
|
||||||
@ -166,12 +166,12 @@ func validHostname(h string) bool {
|
|||||||
if len(h) > 253 {
|
if len(h) > 253 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, label := range strings.Split(h, ".") {
|
for label := range strings.SplitSeq(h, ".") {
|
||||||
if label == "" || len(label) > 63 {
|
if label == "" || len(label) > 63 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, r := range label {
|
for _, r := range label {
|
||||||
if !(r >= 'a' && r <= 'z') && !(r >= '0' && r <= '9') && r != '-' {
|
if (r < 'a' || r > 'z') && (r < '0' || r > '9') && r != '-' {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,26 @@ package plugin
|
|||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
|
type bridgeCallerPluginContextKey struct{}
|
||||||
|
|
||||||
|
// WithBridgeCallerPlugin records the host-authenticated plugin name of a
|
||||||
|
// bridge caller. Hosts and ABI adapters must derive pluginName from the loaded
|
||||||
|
// caller, never from guest-controlled request data.
|
||||||
|
func WithBridgeCallerPlugin(ctx context.Context, pluginName string) context.Context {
|
||||||
|
return context.WithValue(ctx, bridgeCallerPluginContextKey{}, pluginName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BridgeCallerPlugin returns the host-authenticated plugin name of the
|
||||||
|
// current bridge caller. Ownership-sensitive providers should fail closed
|
||||||
|
// when no caller identity is present.
|
||||||
|
func BridgeCallerPlugin(ctx context.Context) (string, bool) {
|
||||||
|
if ctx == nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
pluginName, ok := ctx.Value(bridgeCallerPluginContextKey{}).(string)
|
||||||
|
return pluginName, ok && pluginName != ""
|
||||||
|
}
|
||||||
|
|
||||||
// PluginBridge allows plugins to share services with each other.
|
// PluginBridge allows plugins to share services with each other.
|
||||||
// Plugins register named services during startup; other plugins look them up at runtime.
|
// Plugins register named services during startup; other plugins look them up at runtime.
|
||||||
//
|
//
|
||||||
|
|||||||
25
plugin/bridge_test.go
Normal file
25
plugin/bridge_test.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBridgeCallerPlugin(t *testing.T) {
|
||||||
|
if got, ok := BridgeCallerPlugin(context.Background()); ok || got != "" {
|
||||||
|
t.Fatalf("BridgeCallerPlugin(background) = %q, %t, want empty, false", got, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := WithBridgeCallerPlugin(context.Background(), "website")
|
||||||
|
if got, ok := BridgeCallerPlugin(ctx); !ok || got != "website" {
|
||||||
|
t.Fatalf("BridgeCallerPlugin(ctx) = %q, %t, want website, true", got, ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithBridgeCallerPluginEmptyDoesNotAuthenticateCaller(t *testing.T) {
|
||||||
|
ctx := WithBridgeCallerPlugin(context.Background(), "spoofed")
|
||||||
|
ctx = WithBridgeCallerPlugin(ctx, "")
|
||||||
|
if got, ok := BridgeCallerPlugin(ctx); ok || got != "" {
|
||||||
|
t.Fatalf("BridgeCallerPlugin(ctx) = %q, %t, want empty, false", got, ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
plugin/load_once.go
Normal file
26
plugin/load_once.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MaxLoadOnceKeyBytes = 128
|
||||||
|
|
||||||
|
var loadOnceKeyPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]*$`)
|
||||||
|
|
||||||
|
// ValidateLoadOnceKeys checks the stable identity contract shared by plugin
|
||||||
|
// authors, the packer, and the host loader.
|
||||||
|
func ValidateLoadOnceKeys(keys []string) error {
|
||||||
|
seen := make(map[string]bool, len(keys))
|
||||||
|
for _, key := range keys {
|
||||||
|
if len(key) == 0 || len(key) > MaxLoadOnceKeyBytes || !loadOnceKeyPattern.MatchString(key) {
|
||||||
|
return fmt.Errorf("invalid load-once key %q: must be 1-%d bytes of lowercase letters, digits, dot, underscore, or hyphen, starting alphanumeric", key, MaxLoadOnceKeyBytes)
|
||||||
|
}
|
||||||
|
if seen[key] {
|
||||||
|
return fmt.Errorf("duplicate load-once key %q", key)
|
||||||
|
}
|
||||||
|
seen[key] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
27
plugin/load_once_test.go
Normal file
27
plugin/load_once_test.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestValidateLoadOnceKeys(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
keys []string
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{name: "valid stable keys", keys: []string{"documentation-menu.v1", "seed_defaults-2"}, ok: true},
|
||||||
|
{name: "empty", keys: []string{""}},
|
||||||
|
{name: "uppercase", keys: []string{"DocumentationMenu.v1"}},
|
||||||
|
{name: "duplicate", keys: []string{"menu.v1", "menu.v1"}},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
err := ValidateLoadOnceKeys(test.keys)
|
||||||
|
if test.ok && err != nil {
|
||||||
|
t.Fatalf("ValidateLoadOnceKeys() error = %v", err)
|
||||||
|
}
|
||||||
|
if !test.ok && err == nil {
|
||||||
|
t.Fatal("ValidateLoadOnceKeys() error = nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -91,6 +91,8 @@ type ModPublicRoute struct {
|
|||||||
|
|
||||||
type ModCompat struct {
|
type ModCompat struct {
|
||||||
BlockCore string `toml:"block_core"`
|
BlockCore string `toml:"block_core"`
|
||||||
|
AdminAPI string `toml:"admin_api"`
|
||||||
|
PluginSDK string `toml:"plugin_sdk"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ModRequirement struct {
|
type ModRequirement struct {
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
tomlpkg "github.com/BurntSushi/toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseModFull_BasicFields(t *testing.T) {
|
func TestParseModFull_BasicFields(t *testing.T) {
|
||||||
@ -278,6 +281,8 @@ version = "0.2.0"
|
|||||||
|
|
||||||
[compatibility]
|
[compatibility]
|
||||||
block_core = ">=1.5 <2.0"
|
block_core = ">=1.5 <2.0"
|
||||||
|
admin_api = ">=0.1.2 <0.2.0"
|
||||||
|
plugin_sdk = ">=0.3.7 <0.4.0"
|
||||||
|
|
||||||
[[requires]]
|
[[requires]]
|
||||||
name = "@blockninja/smartblock"
|
name = "@blockninja/smartblock"
|
||||||
@ -291,8 +296,17 @@ version = ">=1.2"
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ParseModFull err: %v", err)
|
t.Fatalf("ParseModFull err: %v", err)
|
||||||
}
|
}
|
||||||
if m.Compatibility == nil || m.Compatibility.BlockCore != ">=1.5 <2.0" {
|
if m.Compatibility == nil {
|
||||||
t.Errorf("Compat = %+v", m.Compatibility)
|
t.Fatal("Compatibility is nil")
|
||||||
|
}
|
||||||
|
if m.Compatibility.BlockCore != ">=1.5 <2.0" {
|
||||||
|
t.Errorf("Compat.BlockCore = %q", m.Compatibility.BlockCore)
|
||||||
|
}
|
||||||
|
if m.Compatibility.AdminAPI != ">=0.1.2 <0.2.0" {
|
||||||
|
t.Errorf("Compat.AdminAPI = %q", m.Compatibility.AdminAPI)
|
||||||
|
}
|
||||||
|
if m.Compatibility.PluginSDK != ">=0.3.7 <0.4.0" {
|
||||||
|
t.Errorf("Compat.PluginSDK = %q", m.Compatibility.PluginSDK)
|
||||||
}
|
}
|
||||||
if len(m.Requires) != 2 {
|
if len(m.Requires) != 2 {
|
||||||
t.Fatalf("Requires len = %d, want 2", len(m.Requires))
|
t.Fatalf("Requires len = %d, want 2", len(m.Requires))
|
||||||
@ -305,6 +319,33 @@ version = ">=1.2"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestModCompatPluginSDKRoundTrip(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
want := &ModFile{
|
||||||
|
Plugin: ModPlugin{Name: "wiki", Version: "0.2.0"},
|
||||||
|
Compatibility: &ModCompat{
|
||||||
|
BlockCore: ">=0.3.4",
|
||||||
|
AdminAPI: ">=0.1.2",
|
||||||
|
PluginSDK: ">=0.3.7 <0.4.0",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var encoded bytes.Buffer
|
||||||
|
if err := tomlpkg.NewEncoder(&encoded).Encode(want); err != nil {
|
||||||
|
t.Fatalf("encode plugin.mod: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(encoded.String(), `plugin_sdk = ">=0.3.7 <0.4.0"`) {
|
||||||
|
t.Fatalf("encoded plugin.mod omitted plugin_sdk:\n%s", encoded.String())
|
||||||
|
}
|
||||||
|
got, err := ParseModFull(encoded.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseModFull(round trip): %v", err)
|
||||||
|
}
|
||||||
|
if got.Compatibility == nil || got.Compatibility.PluginSDK != want.Compatibility.PluginSDK {
|
||||||
|
t.Fatalf("round-trip PluginSDK = %#v, want %q", got.Compatibility, want.Compatibility.PluginSDK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNormalizeTags_HappyPath(t *testing.T) {
|
func TestNormalizeTags_HappyPath(t *testing.T) {
|
||||||
got, err := NormalizeTags([]string{"dark", "agency", "serif"})
|
got, err := NormalizeTags([]string{"dark", "agency", "serif"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -41,8 +41,24 @@ type DataTableConfig struct {
|
|||||||
|
|
||||||
// PageConfig defines a page to provision.
|
// PageConfig defines a page to provision.
|
||||||
type PageConfig struct {
|
type PageConfig struct {
|
||||||
Slug string
|
// Key is the stable, plugin-scoped identity used to find the page across
|
||||||
ParentSlug string
|
// slug and hierarchy changes.
|
||||||
|
Key string
|
||||||
|
|
||||||
|
Slug string
|
||||||
|
|
||||||
|
// ParentKey identifies a parent provisioned by the same plugin.
|
||||||
|
ParentKey string
|
||||||
|
|
||||||
|
// ParentSlug is the legacy slug-based parent reference. New declarations
|
||||||
|
// should use ParentKey so moving or renaming the parent does not break the
|
||||||
|
// relationship.
|
||||||
|
ParentSlug string
|
||||||
|
|
||||||
|
// MountPath is applied when the page is first created. Reconciliation does
|
||||||
|
// not move an existing page when this value later changes.
|
||||||
|
MountPath string
|
||||||
|
|
||||||
Title string
|
Title string
|
||||||
TemplateKey string
|
TemplateKey string
|
||||||
Blocks []PageBlockConfig
|
Blocks []PageBlockConfig
|
||||||
@ -51,6 +67,10 @@ type PageConfig struct {
|
|||||||
DetailSlugField string
|
DetailSlugField string
|
||||||
ReconcileBlocks bool
|
ReconcileBlocks bool
|
||||||
ReconcileTemplate bool
|
ReconcileTemplate bool
|
||||||
|
// MergeMissingBlockContent shallowly adds absent top-level default keys to
|
||||||
|
// matching plugin-owned block assignments. Present values, including empty
|
||||||
|
// strings, zero, false, and null, are preserved.
|
||||||
|
MergeMissingBlockContent bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageBlockConfig defines a block within a provisioned page.
|
// PageBlockConfig defines a block within a provisioned page.
|
||||||
@ -72,10 +92,15 @@ type EmbeddingConfigDef struct {
|
|||||||
|
|
||||||
// MenuItemConfig defines a menu item to provision.
|
// MenuItemConfig defines a menu item to provision.
|
||||||
type MenuItemConfig struct {
|
type MenuItemConfig struct {
|
||||||
Label string
|
Label string
|
||||||
URL string
|
URL string
|
||||||
PageSlug string
|
PageSlug string
|
||||||
SortOrder int32
|
// PagePlugin and PageKey identify a provisioned page independently of its
|
||||||
|
// current slug. PagePlugin permits a menu declared by one plugin to target
|
||||||
|
// a page owned by another.
|
||||||
|
PagePlugin string
|
||||||
|
PageKey string
|
||||||
|
SortOrder int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmbedConfig defines an embeddable component template.
|
// EmbedConfig defines an embeddable component template.
|
||||||
|
|||||||
@ -68,7 +68,7 @@ func checkRoutePath(r ModPublicRoute) string {
|
|||||||
if strings.HasSuffix(p, "/") {
|
if strings.HasSuffix(p, "/") {
|
||||||
return "no trailing slash; set prefix = true to claim the subtree"
|
return "no trailing slash; set prefix = true to claim the subtree"
|
||||||
}
|
}
|
||||||
for _, seg := range strings.Split(p[1:], "/") {
|
for seg := range strings.SplitSeq(p[1:], "/") {
|
||||||
switch {
|
switch {
|
||||||
case seg == "":
|
case seg == "":
|
||||||
return "empty path segment"
|
return "empty path segment"
|
||||||
|
|||||||
@ -13,11 +13,20 @@ import (
|
|||||||
// Plugins register their templates and blocks through the provided registries.
|
// Plugins register their templates and blocks through the provided registries.
|
||||||
type RegisterFunc func(tr templates.TemplateRegistry, br blocks.BlockRegistry) error
|
type RegisterFunc func(tr templates.TemplateRegistry, br blocks.BlockRegistry) error
|
||||||
|
|
||||||
|
// LoadOnceHook is a persistent, keyed load callback. The host runs Run after
|
||||||
|
// the ordinary Load hook and records a successful execution by plugin name and
|
||||||
|
// Key. The same key is never run successfully again on that site; changing the
|
||||||
|
// key explicitly schedules a replacement callback once.
|
||||||
|
type LoadOnceHook struct {
|
||||||
|
Key string
|
||||||
|
Run func(deps CoreServices) error
|
||||||
|
}
|
||||||
|
|
||||||
// PluginRegistration defines a compiled-in plugin's entry points.
|
// PluginRegistration defines a compiled-in plugin's entry points.
|
||||||
type PluginRegistration struct {
|
type PluginRegistration struct {
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
Register RegisterFunc
|
Register RegisterFunc
|
||||||
RegisterWithProvisioner func(tr templates.TemplateRegistry, br blocks.BlockRegistry, p Provisioner) error
|
RegisterWithProvisioner func(tr templates.TemplateRegistry, br blocks.BlockRegistry, p Provisioner) error
|
||||||
|
|
||||||
Assets func() http.Handler
|
Assets func() http.Handler
|
||||||
@ -39,8 +48,9 @@ type PluginRegistration struct {
|
|||||||
DirectoryExtensions func() *DirectoryExtensions
|
DirectoryExtensions func() *DirectoryExtensions
|
||||||
MediaHooks MediaHooksProvider
|
MediaHooks MediaHooksProvider
|
||||||
|
|
||||||
Load func(deps CoreServices) error
|
Load func(deps CoreServices) error
|
||||||
Unload func(ctx context.Context) error
|
LoadOnce []LoadOnceHook
|
||||||
|
Unload func(ctx context.Context) error
|
||||||
|
|
||||||
Dependencies []Dependency
|
Dependencies []Dependency
|
||||||
Migrations func() fs.FS
|
Migrations func() fs.FS
|
||||||
|
|||||||
@ -790,7 +790,9 @@ func TestCapabilityRoundTrip(t *testing.T) {
|
|||||||
resp: &abiv1.ProvisionerEnsurePageResponse{},
|
resp: &abiv1.ProvisionerEnsurePageResponse{},
|
||||||
run: func(t *testing.T, cs plugin.CoreServices) {
|
run: func(t *testing.T, cs plugin.CoreServices) {
|
||||||
err := cs.Provisioner.EnsurePage(plugin.PageConfig{
|
err := cs.Provisioner.EnsurePage(plugin.PageConfig{
|
||||||
Slug: "/", Title: "Home", TemplateKey: "homepage", ReconcileBlocks: true,
|
Key: "home", Slug: "/", ParentKey: "site", ParentSlug: "/legacy-site",
|
||||||
|
MountPath: "/knowledge", Title: "Home", TemplateKey: "homepage",
|
||||||
|
ReconcileBlocks: true, MergeMissingBlockContent: true,
|
||||||
Blocks: []plugin.PageBlockConfig{
|
Blocks: []plugin.PageBlockConfig{
|
||||||
{BlockKey: "html", Title: "Hero", Content: map[string]any{"x": float64(1)}, Slot: "main", SortOrder: 1},
|
{BlockKey: "html", Title: "Hero", Content: map[string]any{"x": float64(1)}, Slot: "main", SortOrder: 1},
|
||||||
},
|
},
|
||||||
@ -813,7 +815,9 @@ func TestCapabilityRoundTrip(t *testing.T) {
|
|||||||
name: "provisioner_ensure_menu_item", wantMethod: "provisioner.ensure_menu_item",
|
name: "provisioner_ensure_menu_item", wantMethod: "provisioner.ensure_menu_item",
|
||||||
resp: &abiv1.ProvisionerEnsureMenuItemResponse{},
|
resp: &abiv1.ProvisionerEnsureMenuItemResponse{},
|
||||||
run: func(t *testing.T, cs plugin.CoreServices) {
|
run: func(t *testing.T, cs plugin.CoreServices) {
|
||||||
err := cs.Provisioner.EnsureMenuItem("main", plugin.MenuItemConfig{Label: "Docs", PageSlug: "/docs", SortOrder: 3})
|
err := cs.Provisioner.EnsureMenuItem("main", plugin.MenuItemConfig{
|
||||||
|
Label: "Docs", PageSlug: "/docs", PagePlugin: "wiki", PageKey: "index", SortOrder: 3,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1008,6 +1012,41 @@ func TestCapabilityRoundTrip(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProvisionerPageContractGolden(t *testing.T) {
|
||||||
|
raw, err := os.ReadFile(goldenPath("provisioner_ensure_page_req"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
req := &abiv1.ProvisionerEnsurePageRequest{}
|
||||||
|
if err := proto.Unmarshal(raw, req); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
page := req.GetPage()
|
||||||
|
if page == nil {
|
||||||
|
t.Fatal("page is nil")
|
||||||
|
}
|
||||||
|
if page.GetKey() != "home" || page.GetSlug() != "/" ||
|
||||||
|
page.GetParentKey() != "site" || page.GetParentSlug() != "/legacy-site" ||
|
||||||
|
page.GetMountPath() != "/knowledge" || !page.GetMergeMissingBlockContent() {
|
||||||
|
t.Errorf("page seed = %+v", page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvisionerMenuPageReferenceContractGolden(t *testing.T) {
|
||||||
|
raw, err := os.ReadFile(goldenPath("provisioner_ensure_menu_item_req"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
req := &abiv1.ProvisionerEnsureMenuItemRequest{}
|
||||||
|
if err := proto.Unmarshal(raw, req); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if req.GetMenuName() != "main" || req.GetPageSlug() != "/docs" ||
|
||||||
|
req.GetPagePlugin() != "wiki" || req.GetPageKey() != "index" {
|
||||||
|
t.Errorf("menu request = %+v", req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fakeAbiErr is a transport error carrying an ABI code, like wasmguest.HostError.
|
// fakeAbiErr is a transport error carrying an ABI code, like wasmguest.HostError.
|
||||||
type fakeAbiErr struct {
|
type fakeAbiErr struct {
|
||||||
code abiv1.AbiErrorCode
|
code abiv1.AbiErrorCode
|
||||||
|
|||||||
@ -53,15 +53,19 @@ func (s *provisionerStub) EnsureSetting(key string, defaultValue any) error {
|
|||||||
|
|
||||||
func (s *provisionerStub) EnsurePage(config plugin.PageConfig) error {
|
func (s *provisionerStub) EnsurePage(config plugin.PageConfig) error {
|
||||||
seed := &abiv1.PageSeed{
|
seed := &abiv1.PageSeed{
|
||||||
Slug: config.Slug,
|
Key: config.Key,
|
||||||
ParentSlug: config.ParentSlug,
|
Slug: config.Slug,
|
||||||
Title: config.Title,
|
ParentKey: config.ParentKey,
|
||||||
TemplateKey: config.TemplateKey,
|
ParentSlug: config.ParentSlug,
|
||||||
DetailSourceType: config.DetailSourceType,
|
MountPath: config.MountPath,
|
||||||
DetailSourceKey: config.DetailSourceKey,
|
Title: config.Title,
|
||||||
DetailSlugField: config.DetailSlugField,
|
TemplateKey: config.TemplateKey,
|
||||||
ReconcileBlocks: config.ReconcileBlocks,
|
DetailSourceType: config.DetailSourceType,
|
||||||
ReconcileTemplate: config.ReconcileTemplate,
|
DetailSourceKey: config.DetailSourceKey,
|
||||||
|
DetailSlugField: config.DetailSlugField,
|
||||||
|
ReconcileBlocks: config.ReconcileBlocks,
|
||||||
|
ReconcileTemplate: config.ReconcileTemplate,
|
||||||
|
MergeMissingBlockContent: config.MergeMissingBlockContent,
|
||||||
}
|
}
|
||||||
for _, b := range config.Blocks {
|
for _, b := range config.Blocks {
|
||||||
contentJSON, err := json.Marshal(b.Content)
|
contentJSON, err := json.Marshal(b.Content)
|
||||||
@ -92,11 +96,13 @@ func (s *provisionerStub) OverrideSiteSettings(overrides map[string]any) error {
|
|||||||
|
|
||||||
func (s *provisionerStub) EnsureMenuItem(menuName string, config plugin.MenuItemConfig) error {
|
func (s *provisionerStub) EnsureMenuItem(menuName string, config plugin.MenuItemConfig) error {
|
||||||
req := &abiv1.ProvisionerEnsureMenuItemRequest{
|
req := &abiv1.ProvisionerEnsureMenuItemRequest{
|
||||||
MenuName: menuName,
|
MenuName: menuName,
|
||||||
Label: config.Label,
|
Label: config.Label,
|
||||||
Url: config.URL,
|
Url: config.URL,
|
||||||
PageSlug: config.PageSlug,
|
PageSlug: config.PageSlug,
|
||||||
SortOrder: config.SortOrder,
|
PagePlugin: config.PagePlugin,
|
||||||
|
PageKey: config.PageKey,
|
||||||
|
SortOrder: config.SortOrder,
|
||||||
}
|
}
|
||||||
return s.invoke(context.Background(), "ensure_menu_item", req, &abiv1.ProvisionerEnsureMenuItemResponse{})
|
return s.invoke(context.Background(), "ensure_menu_item", req, &abiv1.ProvisionerEnsureMenuItemResponse{})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
|
|
||||||
mainDocs"/docs(
|
mainDocs"/docs(2wiki:index
|
||||||
@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
4
|
\
|
||||||
/Home"homepage*
|
//legacy-siteHome"homepage*
|
||||||
htmlHero{"x":1}*main0H
|
htmlHero{"x":1}*main0HZhomebsitej
|
||||||
|
/knowledgep
|
||||||
@ -37,11 +37,13 @@ func contextFromRenderContext(ctx context.Context, rc *abiv1.RenderContext) cont
|
|||||||
}
|
}
|
||||||
if p := rc.GetPage(); p != nil {
|
if p := rc.GetPage(); p != nil {
|
||||||
ctx = blocks.WithCurrentPage(ctx, &blocks.PageContext{
|
ctx = blocks.WithCurrentPage(ctx, &blocks.PageContext{
|
||||||
ID: parseUUID(p.GetId()),
|
ID: parseUUID(p.GetId()),
|
||||||
Slug: p.GetSlug(),
|
Slug: p.GetSlug(),
|
||||||
Title: p.GetTitle(),
|
DerivedSlug: p.GetDerivedSlug(),
|
||||||
PostType: p.GetPostType(),
|
MountPath: p.GetMountPath(),
|
||||||
Status: p.GetStatus(),
|
Title: p.GetTitle(),
|
||||||
|
PostType: p.GetPostType(),
|
||||||
|
Status: p.GetStatus(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if p := rc.GetPost(); p != nil {
|
if p := rc.GetPost(); p != nil {
|
||||||
@ -159,6 +161,8 @@ func blockContextFromProto(pb *abiv1.BlockContext) *blocks.BlockContext {
|
|||||||
URL: pb.GetUrl(),
|
URL: pb.GetUrl(),
|
||||||
Path: pb.GetPath(),
|
Path: pb.GetPath(),
|
||||||
Slug: pb.GetSlug(),
|
Slug: pb.GetSlug(),
|
||||||
|
DerivedSlug: pb.GetDerivedSlug(),
|
||||||
|
MountPath: pb.GetMountPath(),
|
||||||
PageID: pb.GetPageId(),
|
PageID: pb.GetPageId(),
|
||||||
PageTitle: pb.GetPageTitle(),
|
PageTitle: pb.GetPageTitle(),
|
||||||
TemplateKey: pb.GetTemplateKey(),
|
TemplateKey: pb.GetTemplateKey(),
|
||||||
|
|||||||
51
plugin/wasmguest/context_test.go
Normal file
51
plugin/wasmguest/context_test.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package wasmguest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
|
||||||
|
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContextFromRenderContextCarriesResolvedPagePaths(t *testing.T) {
|
||||||
|
pageID := uuid.MustParse("5f0c5f3f-3f1e-4a3a-9b6e-2f4f6a6d7e8f")
|
||||||
|
ctx := contextFromRenderContext(context.Background(), &abiv1.RenderContext{
|
||||||
|
Page: &abiv1.PageContext{
|
||||||
|
Id: pageID.String(),
|
||||||
|
Slug: "/article",
|
||||||
|
DerivedSlug: "/knowledge/article",
|
||||||
|
MountPath: "/knowledge",
|
||||||
|
Title: "Article",
|
||||||
|
PostType: "page",
|
||||||
|
Status: "published",
|
||||||
|
},
|
||||||
|
BlockContext: &abiv1.BlockContext{
|
||||||
|
Slug: "/article",
|
||||||
|
DerivedSlug: "/knowledge/article",
|
||||||
|
MountPath: "/knowledge",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
page := blocks.GetCurrentPage(ctx)
|
||||||
|
if page == nil {
|
||||||
|
t.Fatal("current page is nil")
|
||||||
|
}
|
||||||
|
if page.ID != pageID || page.Slug != "/article" ||
|
||||||
|
page.DerivedSlug != "/knowledge/article" || page.MountPath != "/knowledge" {
|
||||||
|
t.Errorf("page context = %+v", page)
|
||||||
|
}
|
||||||
|
|
||||||
|
block := blocks.GetBlockContext(ctx)
|
||||||
|
if block == nil {
|
||||||
|
t.Fatal("block context is nil")
|
||||||
|
}
|
||||||
|
if block.Slug != "/article" || block.DerivedSlug != "/knowledge/article" || block.MountPath != "/knowledge" {
|
||||||
|
t.Errorf("block context = %+v", block)
|
||||||
|
}
|
||||||
|
data := block.ToMap()
|
||||||
|
if data["derivedSlug"] != "/knowledge/article" || data["mountPath"] != "/knowledge" {
|
||||||
|
t.Errorf("block template map = %#v", data)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -147,6 +147,9 @@ func (g *guest) buildManifest() *abiv1.PluginManifest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.RequiredIconPacks = append(m.RequiredIconPacks, reg.RequiredIconPacks...)
|
m.RequiredIconPacks = append(m.RequiredIconPacks, reg.RequiredIconPacks...)
|
||||||
|
for _, hook := range reg.LoadOnce {
|
||||||
|
m.LoadOnceKeys = append(m.LoadOnceKeys, hook.Key)
|
||||||
|
}
|
||||||
|
|
||||||
// Custom template tags/filters the plugin registered via blocks.RegisterTag
|
// Custom template tags/filters the plugin registered via blocks.RegisterTag
|
||||||
// / blocks.RegisterFilter during the Register pass (runRegister reset the
|
// / blocks.RegisterFilter during the Register pass (runRegister reset the
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import (
|
|||||||
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
|
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/ai"
|
"git.dev.alexdunmow.com/block/pluginsdk/ai"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
||||||
|
"git.dev.alexdunmow.com/block/pluginsdk/document"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/plugin"
|
"git.dev.alexdunmow.com/block/pluginsdk/plugin"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/plugin/wasmguest/bnwasm"
|
"git.dev.alexdunmow.com/block/pluginsdk/plugin/wasmguest/bnwasm"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/plugin/wasmguest/caps"
|
"git.dev.alexdunmow.com/block/pluginsdk/plugin/wasmguest/caps"
|
||||||
@ -99,7 +100,15 @@ func newGuest(reg plugin.PluginRegistration) *guest {
|
|||||||
// Pool whose calls fail cleanly, matching the capability stubs.
|
// Pool whose calls fail cleanly, matching the capability stubs.
|
||||||
g.services.Pool = bnwasm.NewPool(bnwasm.Transport(capTransport))
|
g.services.Pool = bnwasm.NewPool(bnwasm.Transport(capTransport))
|
||||||
g.rag, _ = g.services.RAGService.(*caps.RAGStub)
|
g.rag, _ = g.services.RAGService.(*caps.RAGStub)
|
||||||
|
// Register callbacks may use HostServices for capability-backed surfaces
|
||||||
|
// that are not passed as Register parameters, such as bridge services.
|
||||||
|
// Publish this partially initialized guest only for the synchronous
|
||||||
|
// registration pass, then restore the previous runtime. Serve installs g
|
||||||
|
// permanently after newGuest returns.
|
||||||
|
previous := current
|
||||||
|
current = g
|
||||||
g.registerErr = runRegister(reg, g.templates, g.blocks)
|
g.registerErr = runRegister(reg, g.templates, g.blocks)
|
||||||
|
current = previous
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,6 +194,8 @@ func (g *guest) invoke(hookID uint32, req []byte) (out []byte) {
|
|||||||
payload, abiErr = g.runJob(ctx, env.GetPayload())
|
payload, abiErr = g.runJob(ctx, env.GetPayload())
|
||||||
case abiv1.Hook_HOOK_LOAD:
|
case abiv1.Hook_HOOK_LOAD:
|
||||||
payload, abiErr = g.load(env.GetPayload())
|
payload, abiErr = g.load(env.GetPayload())
|
||||||
|
case abiv1.Hook_HOOK_LOAD_ONCE:
|
||||||
|
payload, abiErr = g.loadOnce(env.GetPayload())
|
||||||
case abiv1.Hook_HOOK_UNLOAD:
|
case abiv1.Hook_HOOK_UNLOAD:
|
||||||
payload, abiErr = g.unload(ctx)
|
payload, abiErr = g.unload(ctx)
|
||||||
case abiv1.Hook_HOOK_RAG_FETCH:
|
case abiv1.Hook_HOOK_RAG_FETCH:
|
||||||
@ -251,7 +262,12 @@ func (g *guest) renderBlock(ctx context.Context, payload []byte) (proto.Message,
|
|||||||
return nil, internalError("marshal powered block data: " + err.Error())
|
return nil, internalError("marshal powered block data: " + err.Error())
|
||||||
}
|
}
|
||||||
return &abiv1.RenderBlockResponse{
|
return &abiv1.RenderBlockResponse{
|
||||||
Powered: &abiv1.PoweredBlock{Template: pr.Template, DataJson: dataJSON},
|
Powered: &abiv1.PoweredBlock{
|
||||||
|
Template: pr.Template,
|
||||||
|
DataJson: dataJSON,
|
||||||
|
StatusCode: pr.StatusCode,
|
||||||
|
Metadata: documentMetadataToProto(pr.Metadata),
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return &abiv1.RenderBlockResponse{Html: out}, nil
|
return &abiv1.RenderBlockResponse{Html: out}, nil
|
||||||
@ -362,6 +378,8 @@ func (g *guest) renderTemplate(ctx context.Context, payload []byte) (proto.Messa
|
|||||||
tdoc.HtmlAttrs = pd.HTMLAttrs
|
tdoc.HtmlAttrs = pd.HTMLAttrs
|
||||||
tdoc.HeadExtraHtml = head
|
tdoc.HeadExtraHtml = head
|
||||||
tdoc.BodyEndExtraHtml = end
|
tdoc.BodyEndExtraHtml = end
|
||||||
|
tdoc.StatusCode = pd.StatusCode
|
||||||
|
tdoc.Metadata = documentMetadataToProto(pd.Metadata)
|
||||||
} else {
|
} else {
|
||||||
body, err := renderPart(component)
|
body, err := renderPart(component)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -372,6 +390,19 @@ func (g *guest) renderTemplate(ctx context.Context, payload []byte) (proto.Messa
|
|||||||
return &abiv1.RenderTemplateResponse{Document: tdoc}, nil
|
return &abiv1.RenderTemplateResponse{Document: tdoc}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func documentMetadataToProto(metadata *document.Metadata) *abiv1.DocumentMetadata {
|
||||||
|
if metadata == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &abiv1.DocumentMetadata{
|
||||||
|
Title: metadata.Title,
|
||||||
|
Description: metadata.Description,
|
||||||
|
CanonicalUrl: metadata.CanonicalURL,
|
||||||
|
ImageUrl: metadata.ImageURL,
|
||||||
|
Robots: metadata.Robots,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (g *guest) handleHTTP(ctx context.Context, payload []byte) (proto.Message, *abiv1.AbiError) {
|
func (g *guest) handleHTTP(ctx context.Context, payload []byte) (proto.Message, *abiv1.AbiError) {
|
||||||
if g.reg.HTTPHandler == nil {
|
if g.reg.HTTPHandler == nil {
|
||||||
return nil, &abiv1.AbiError{
|
return nil, &abiv1.AbiError{
|
||||||
@ -470,6 +501,29 @@ func (g *guest) load(payload []byte) (proto.Message, *abiv1.AbiError) {
|
|||||||
return &abiv1.LoadResponse{}, nil
|
return &abiv1.LoadResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *guest) loadOnce(payload []byte) (proto.Message, *abiv1.AbiError) {
|
||||||
|
req := &abiv1.LoadOnceRequest{}
|
||||||
|
if err := proto.Unmarshal(payload, req); err != nil {
|
||||||
|
return nil, decodeError("LoadOnceRequest", err)
|
||||||
|
}
|
||||||
|
for _, hook := range g.reg.LoadOnce {
|
||||||
|
if hook.Key != req.GetKey() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if hook.Run == nil {
|
||||||
|
return nil, internalError("load-once hook " + hook.Key + " has no callback")
|
||||||
|
}
|
||||||
|
if err := hook.Run(g.services); err != nil {
|
||||||
|
return nil, internalError(err.Error())
|
||||||
|
}
|
||||||
|
return &abiv1.LoadOnceResponse{}, nil
|
||||||
|
}
|
||||||
|
return nil, &abiv1.AbiError{
|
||||||
|
Code: abiv1.AbiErrorCode_ABI_ERROR_CODE_UNIMPLEMENTED,
|
||||||
|
Message: "unknown load-once key " + req.GetKey(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (g *guest) unload(ctx context.Context) (proto.Message, *abiv1.AbiError) {
|
func (g *guest) unload(ctx context.Context) (proto.Message, *abiv1.AbiError) {
|
||||||
if g.reg.Unload != nil {
|
if g.reg.Unload != nil {
|
||||||
if err := g.reg.Unload(ctx); err != nil {
|
if err := g.reg.Unload(ctx); err != nil {
|
||||||
@ -667,6 +721,7 @@ func (g *guest) bridgeCall(ctx context.Context, payload []byte) (proto.Message,
|
|||||||
Message: "bridge service " + req.GetServiceName() + " does not implement plugin.BridgeInvokable",
|
Message: "bridge service " + req.GetServiceName() + " does not implement plugin.BridgeInvokable",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctx = plugin.WithBridgeCallerPlugin(ctx, req.GetCallerPlugin())
|
||||||
out, err := invokable.InvokeBridge(ctx, req.GetMethod(), req.GetPayload())
|
out, err := invokable.InvokeBridge(ctx, req.GetMethod(), req.GetPayload())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, internalError(err.Error())
|
return nil, internalError(err.Error())
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
|
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
||||||
|
"git.dev.alexdunmow.com/block/pluginsdk/document"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/plugin"
|
"git.dev.alexdunmow.com/block/pluginsdk/plugin"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/templates"
|
"git.dev.alexdunmow.com/block/pluginsdk/templates"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
@ -24,6 +25,12 @@ func (c textComponent) Render(_ context.Context, w io.Writer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type bridgeInvokableFunc func(context.Context, string, []byte) ([]byte, error)
|
||||||
|
|
||||||
|
func (f bridgeInvokableFunc) InvokeBridge(ctx context.Context, method string, payload []byte) ([]byte, error) {
|
||||||
|
return f(ctx, method, payload)
|
||||||
|
}
|
||||||
|
|
||||||
func fixtureRegistration() plugin.PluginRegistration {
|
func fixtureRegistration() plugin.PluginRegistration {
|
||||||
return plugin.PluginRegistration{
|
return plugin.PluginRegistration{
|
||||||
Name: "fixture",
|
Name: "fixture",
|
||||||
@ -123,6 +130,76 @@ func TestServeInstallsRuntime(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServeExposesHostServicesDuringRegister(t *testing.T) {
|
||||||
|
previous := current
|
||||||
|
t.Cleanup(func() { current = previous })
|
||||||
|
|
||||||
|
Serve(plugin.PluginRegistration{
|
||||||
|
Name: "bridge-provider",
|
||||||
|
Version: "0.1.0",
|
||||||
|
Register: func(_ templates.TemplateRegistry, _ blocks.BlockRegistry) error {
|
||||||
|
bridge := HostServices().Bridge
|
||||||
|
if bridge == nil {
|
||||||
|
return fmt.Errorf("HostServices bridge is unavailable during Register")
|
||||||
|
}
|
||||||
|
bridge.RegisterService("bridge-provider", "content", bridgeInvokableFunc(
|
||||||
|
func(_ context.Context, _ string, _ []byte) ([]byte, error) {
|
||||||
|
return []byte("registered"), nil
|
||||||
|
},
|
||||||
|
))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
resp := invokeHook(t, current, abiv1.Hook_HOOK_BRIDGE_CALL, &abiv1.BridgeCallRequest{
|
||||||
|
ServiceName: "content",
|
||||||
|
Method: "apply",
|
||||||
|
})
|
||||||
|
if resp.GetError() != nil {
|
||||||
|
t.Fatalf("bridge call returned error: %v", resp.GetError())
|
||||||
|
}
|
||||||
|
bridgeResp := &abiv1.BridgeCallResponse{}
|
||||||
|
if err := proto.Unmarshal(resp.GetPayload(), bridgeResp); err != nil {
|
||||||
|
t.Fatalf("unmarshal BridgeCallResponse: %v", err)
|
||||||
|
}
|
||||||
|
if got := string(bridgeResp.GetPayload()); got != "registered" {
|
||||||
|
t.Fatalf("bridge payload = %q, want registered", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBridgeCallProvidesAuthenticatedCallerContext(t *testing.T) {
|
||||||
|
g := newGuest(fixtureRegistration())
|
||||||
|
g.services.Bridge.RegisterService("fixture", "content", bridgeInvokableFunc(
|
||||||
|
func(ctx context.Context, method string, payload []byte) ([]byte, error) {
|
||||||
|
caller, ok := plugin.BridgeCallerPlugin(ctx)
|
||||||
|
if !ok || caller != "website" {
|
||||||
|
t.Fatalf("BridgeCallerPlugin(ctx) = %q, %t, want website, true", caller, ok)
|
||||||
|
}
|
||||||
|
if method != "ApplyBundle" || string(payload) != `{"revision":"1"}` {
|
||||||
|
t.Fatalf("bridge call = %q %q", method, payload)
|
||||||
|
}
|
||||||
|
return []byte(`{"created":1}`), nil
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
|
resp := invokeHook(t, g, abiv1.Hook_HOOK_BRIDGE_CALL, &abiv1.BridgeCallRequest{
|
||||||
|
ServiceName: "content",
|
||||||
|
Method: "ApplyBundle",
|
||||||
|
Payload: []byte(`{"revision":"1"}`),
|
||||||
|
CallerPlugin: "website",
|
||||||
|
})
|
||||||
|
if resp.GetError() != nil {
|
||||||
|
t.Fatalf("bridge call returned error: %v", resp.GetError())
|
||||||
|
}
|
||||||
|
bridgeResp := &abiv1.BridgeCallResponse{}
|
||||||
|
if err := proto.Unmarshal(resp.GetPayload(), bridgeResp); err != nil {
|
||||||
|
t.Fatalf("unmarshal BridgeCallResponse: %v", err)
|
||||||
|
}
|
||||||
|
if got := string(bridgeResp.GetPayload()); got != `{"created":1}` {
|
||||||
|
t.Fatalf("bridge payload = %q, want report", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDescribeManifest(t *testing.T) {
|
func TestDescribeManifest(t *testing.T) {
|
||||||
g := newGuest(fixtureRegistration())
|
g := newGuest(fixtureRegistration())
|
||||||
resp := invokeHook(t, g, abiv1.Hook_HOOK_DESCRIBE, &abiv1.DescribeRequest{HostAbiVersion: 1})
|
resp := invokeHook(t, g, abiv1.Hook_HOOK_DESCRIBE, &abiv1.DescribeRequest{HostAbiVersion: 1})
|
||||||
@ -383,6 +460,14 @@ func TestRenderTemplatePageDocumentFillsEnvelope(t *testing.T) {
|
|||||||
HTMLAttrs: map[string]string{"data-theme": "x"},
|
HTMLAttrs: map[string]string{"data-theme": "x"},
|
||||||
HeadExtra: textComponent(`<meta name="e">`),
|
HeadExtra: textComponent(`<meta name="e">`),
|
||||||
BodyEndExtra: textComponent("<script>1</script>"),
|
BodyEndExtra: textComponent("<script>1</script>"),
|
||||||
|
StatusCode: 201,
|
||||||
|
Metadata: &document.Metadata{
|
||||||
|
Title: "Page title",
|
||||||
|
Description: "Page description",
|
||||||
|
CanonicalURL: "https://example.com/page",
|
||||||
|
ImageURL: "https://example.com/page.png",
|
||||||
|
Robots: "index,follow",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -419,6 +504,17 @@ func TestRenderTemplatePageDocumentFillsEnvelope(t *testing.T) {
|
|||||||
if string(doc.GetBodyEndExtraHtml()) != "<script>1</script>" {
|
if string(doc.GetBodyEndExtraHtml()) != "<script>1</script>" {
|
||||||
t.Errorf("body_end_extra_html = %q", doc.GetBodyEndExtraHtml())
|
t.Errorf("body_end_extra_html = %q", doc.GetBodyEndExtraHtml())
|
||||||
}
|
}
|
||||||
|
if doc.GetStatusCode() != 201 {
|
||||||
|
t.Errorf("status_code = %d, want 201", doc.GetStatusCode())
|
||||||
|
}
|
||||||
|
metadata := doc.GetMetadata()
|
||||||
|
if metadata == nil || metadata.GetTitle() != "Page title" ||
|
||||||
|
metadata.GetDescription() != "Page description" ||
|
||||||
|
metadata.GetCanonicalUrl() != "https://example.com/page" ||
|
||||||
|
metadata.GetImageUrl() != "https://example.com/page.png" ||
|
||||||
|
metadata.GetRobots() != "index,follow" {
|
||||||
|
t.Errorf("metadata = %+v", metadata)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJobHook(t *testing.T) {
|
func TestJobHook(t *testing.T) {
|
||||||
@ -451,3 +547,41 @@ func TestLoadUnloadHooks(t *testing.T) {
|
|||||||
t.Fatalf("unload error: %v", resp.GetError())
|
t.Fatalf("unload error: %v", resp.GetError())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDescribeAndDispatchLoadOnceHook(t *testing.T) {
|
||||||
|
calls := 0
|
||||||
|
reg := fixtureRegistration()
|
||||||
|
reg.LoadOnce = []plugin.LoadOnceHook{{
|
||||||
|
Key: "documentation-menu.v1",
|
||||||
|
Run: func(plugin.CoreServices) error {
|
||||||
|
calls++
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
g := newGuest(reg)
|
||||||
|
|
||||||
|
describe := invokeHook(t, g, abiv1.Hook_HOOK_DESCRIBE, &abiv1.DescribeRequest{HostAbiVersion: 1})
|
||||||
|
if describe.GetError() != nil {
|
||||||
|
t.Fatalf("describe error: %v", describe.GetError())
|
||||||
|
}
|
||||||
|
dr := &abiv1.DescribeResponse{}
|
||||||
|
if err := proto.Unmarshal(describe.GetPayload(), dr); err != nil {
|
||||||
|
t.Fatalf("unmarshal DescribeResponse: %v", err)
|
||||||
|
}
|
||||||
|
if got := dr.GetManifest().GetLoadOnceKeys(); len(got) != 1 || got[0] != "documentation-menu.v1" {
|
||||||
|
t.Fatalf("load_once_keys = %v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := invokeHook(t, g, abiv1.Hook_HOOK_LOAD_ONCE, &abiv1.LoadOnceRequest{Key: "documentation-menu.v1"})
|
||||||
|
if resp.GetError() != nil {
|
||||||
|
t.Fatalf("load-once error: %v", resp.GetError())
|
||||||
|
}
|
||||||
|
if calls != 1 {
|
||||||
|
t.Fatalf("calls = %d, want 1", calls)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = invokeHook(t, g, abiv1.Hook_HOOK_LOAD_ONCE, &abiv1.LoadOnceRequest{Key: "unknown.v1"})
|
||||||
|
if resp.GetError().GetCode() != abiv1.AbiErrorCode_ABI_ERROR_CODE_UNIMPLEMENTED {
|
||||||
|
t.Fatalf("unknown key error = %v, want UNIMPLEMENTED", resp.GetError())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -74,8 +74,8 @@ func methodDocumentation(method protoreflect.MethodDescriptor) (description, doc
|
|||||||
}
|
}
|
||||||
paragraph = strings.TrimSpace(strings.ReplaceAll(paragraph, "\n", " "))
|
paragraph = strings.TrimSpace(strings.ReplaceAll(paragraph, "\n", " "))
|
||||||
methodName := string(method.Name())
|
methodName := string(method.Name())
|
||||||
if strings.HasPrefix(paragraph, methodName+" ") {
|
if after, ok := strings.CutPrefix(paragraph, methodName+" "); ok {
|
||||||
paragraph = strings.TrimSpace(strings.TrimPrefix(paragraph, methodName+" "))
|
paragraph = strings.TrimSpace(after)
|
||||||
if paragraph != "" {
|
if paragraph != "" {
|
||||||
runes := []rune(paragraph)
|
runes := []rune(paragraph)
|
||||||
runes[0] = unicode.ToUpper(runes[0])
|
runes[0] = unicode.ToUpper(runes[0])
|
||||||
|
|||||||
@ -65,30 +65,30 @@ func registerMCPFixture(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
file, err := protodesc.NewFile(&descriptorpb.FileDescriptorProto{
|
file, err := protodesc.NewFile(&descriptorpb.FileDescriptorProto{
|
||||||
Name: proto.String("test/mcpfixture.proto"),
|
Name: new("test/mcpfixture.proto"),
|
||||||
Package: proto.String("mcpfixture.v1"),
|
Package: new("mcpfixture.v1"),
|
||||||
Syntax: proto.String("proto3"),
|
Syntax: new("proto3"),
|
||||||
Dependency: []string{"google/protobuf/timestamp.proto"},
|
Dependency: []string{"google/protobuf/timestamp.proto"},
|
||||||
MessageType: []*descriptorpb.DescriptorProto{{
|
MessageType: []*descriptorpb.DescriptorProto{{
|
||||||
Name: proto.String("ListArticlesRequest"),
|
Name: new("ListArticlesRequest"),
|
||||||
Field: []*descriptorpb.FieldDescriptorProto{
|
Field: []*descriptorpb.FieldDescriptorProto{
|
||||||
{Name: proto.String("search"), Number: proto.Int32(1), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum()},
|
{Name: new("search"), Number: proto.Int32(1), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum()},
|
||||||
{Name: proto.String("page"), Number: proto.Int32(2), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum()},
|
{Name: new("page"), Number: proto.Int32(2), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum()},
|
||||||
{Name: proto.String("published_after"), Number: proto.Int32(3), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), TypeName: proto.String(".google.protobuf.Timestamp")},
|
{Name: new("published_after"), Number: proto.Int32(3), Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), TypeName: new(".google.protobuf.Timestamp")},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Name: proto.String("ListArticlesResponse"),
|
Name: new("ListArticlesResponse"),
|
||||||
}},
|
}},
|
||||||
Service: []*descriptorpb.ServiceDescriptorProto{{
|
Service: []*descriptorpb.ServiceDescriptorProto{{
|
||||||
Name: proto.String("WikiService"),
|
Name: new("WikiService"),
|
||||||
Method: []*descriptorpb.MethodDescriptorProto{{
|
Method: []*descriptorpb.MethodDescriptorProto{{
|
||||||
Name: proto.String("ListArticles"), InputType: proto.String(".mcpfixture.v1.ListArticlesRequest"), OutputType: proto.String(".mcpfixture.v1.ListArticlesResponse"),
|
Name: new("ListArticles"), InputType: new(".mcpfixture.v1.ListArticlesRequest"), OutputType: new(".mcpfixture.v1.ListArticlesResponse"),
|
||||||
}, {
|
}, {
|
||||||
Name: proto.String("StreamArticles"), InputType: proto.String(".mcpfixture.v1.ListArticlesRequest"), OutputType: proto.String(".mcpfixture.v1.ListArticlesResponse"), ServerStreaming: proto.Bool(true),
|
Name: new("StreamArticles"), InputType: new(".mcpfixture.v1.ListArticlesRequest"), OutputType: new(".mcpfixture.v1.ListArticlesResponse"), ServerStreaming: new(true),
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
SourceCodeInfo: &descriptorpb.SourceCodeInfo{Location: []*descriptorpb.SourceCodeInfo_Location{{
|
SourceCodeInfo: &descriptorpb.SourceCodeInfo{Location: []*descriptorpb.SourceCodeInfo_Location{{
|
||||||
Path: []int32{6, 0, 2, 0}, Span: []int32{1, 0, 1, 1}, LeadingComments: proto.String("ListArticles lists Wiki articles.\n\nSupports pagination."),
|
Path: []int32{6, 0, 2, 0}, Span: []int32{1, 0, 1, 1}, LeadingComments: new("ListArticles lists Wiki articles.\n\nSupports pagination."),
|
||||||
}}},
|
}}},
|
||||||
}, protoregistry.GlobalFiles)
|
}, protoregistry.GlobalFiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
|
abiv1 "git.dev.alexdunmow.com/block/pluginsdk/abi/v1"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
||||||
|
"git.dev.alexdunmow.com/block/pluginsdk/document"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/plugin"
|
"git.dev.alexdunmow.com/block/pluginsdk/plugin"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/templates"
|
"git.dev.alexdunmow.com/block/pluginsdk/templates"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
@ -25,7 +26,20 @@ func tagFilterRegistration() plugin.PluginRegistration {
|
|||||||
func(ctx context.Context, content map[string]any) string {
|
func(ctx context.Context, content map[string]any) string {
|
||||||
title, _ := content["title"].(string)
|
title, _ := content["title"].(string)
|
||||||
// Powered: return template + data; the host renders it.
|
// Powered: return template + data; the host renders it.
|
||||||
return blocks.PoweredBlock("<h1>{{ title }}</h1>", map[string]any{"title": title})
|
return blocks.PoweredBlockWithOptions(
|
||||||
|
"<h1>{{ title }}</h1>",
|
||||||
|
map[string]any{"title": title},
|
||||||
|
blocks.PoweredOptions{
|
||||||
|
StatusCode: 404,
|
||||||
|
Metadata: &document.Metadata{
|
||||||
|
Title: "Missing",
|
||||||
|
Description: "Missing article",
|
||||||
|
CanonicalURL: "https://example.com/missing",
|
||||||
|
ImageURL: "https://example.com/missing.png",
|
||||||
|
Robots: "noindex,nofollow",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
blocks.RegisterTag("greet", func(args map[string]any, rctx blocks.RenderContext) (string, error) {
|
blocks.RegisterTag("greet", func(args map[string]any, rctx blocks.RenderContext) (string, error) {
|
||||||
who, _ := args["who"].(string)
|
who, _ := args["who"].(string)
|
||||||
@ -111,6 +125,17 @@ func TestPoweredBlockRoundTrip(t *testing.T) {
|
|||||||
if !strings.Contains(string(p.GetDataJson()), `"title":"Welcome"`) {
|
if !strings.Contains(string(p.GetDataJson()), `"title":"Welcome"`) {
|
||||||
t.Errorf("data_json = %s", p.GetDataJson())
|
t.Errorf("data_json = %s", p.GetDataJson())
|
||||||
}
|
}
|
||||||
|
if p.GetStatusCode() != 404 {
|
||||||
|
t.Errorf("status_code = %d, want 404", p.GetStatusCode())
|
||||||
|
}
|
||||||
|
metadata := p.GetMetadata()
|
||||||
|
if metadata == nil || metadata.GetTitle() != "Missing" ||
|
||||||
|
metadata.GetDescription() != "Missing article" ||
|
||||||
|
metadata.GetCanonicalUrl() != "https://example.com/missing" ||
|
||||||
|
metadata.GetImageUrl() != "https://example.com/missing.png" ||
|
||||||
|
metadata.GetRobots() != "noindex,nofollow" {
|
||||||
|
t.Errorf("metadata = %+v", metadata)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenderTagRoundTrip(t *testing.T) {
|
func TestRenderTagRoundTrip(t *testing.T) {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
||||||
|
"git.dev.alexdunmow.com/block/pluginsdk/document"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/plugin"
|
"git.dev.alexdunmow.com/block/pluginsdk/plugin"
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/templates"
|
"git.dev.alexdunmow.com/block/pluginsdk/templates"
|
||||||
)
|
)
|
||||||
@ -45,7 +46,20 @@ var Registration = plugin.PluginRegistration{
|
|||||||
if powered, _ := content["powered"].(bool); powered {
|
if powered, _ := content["powered"].(bool); powered {
|
||||||
// Powered path: hand the host a template + data instead of final
|
// Powered path: hand the host a template + data instead of final
|
||||||
// HTML, so the host renders it (pongo2) after this call returns.
|
// HTML, so the host renders it (pongo2) after this call returns.
|
||||||
return blocks.PoweredBlock("<p>hello {{ name }}</p>", map[string]any{"name": name})
|
return blocks.PoweredBlockWithOptions(
|
||||||
|
"<p>hello {{ name }}</p>",
|
||||||
|
map[string]any{"name": name},
|
||||||
|
blocks.PoweredOptions{
|
||||||
|
StatusCode: 404,
|
||||||
|
Metadata: &document.Metadata{
|
||||||
|
Title: "Missing greeting",
|
||||||
|
Description: "The greeting was not found.",
|
||||||
|
CanonicalURL: "https://example.com/greetings/missing",
|
||||||
|
ImageURL: "https://example.com/greetings/missing.png",
|
||||||
|
Robots: "noindex,nofollow",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("<p>hello %s</p>", name)
|
return fmt.Sprintf("<p>hello %s</p>", name)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -241,6 +241,17 @@ func TestWasmFixtureTagFilterPoweredRoundTrip(t *testing.T) {
|
|||||||
if !strings.Contains(string(p.GetDataJson()), `"name":"wazero"`) {
|
if !strings.Contains(string(p.GetDataJson()), `"name":"wazero"`) {
|
||||||
t.Errorf("powered data_json = %s", p.GetDataJson())
|
t.Errorf("powered data_json = %s", p.GetDataJson())
|
||||||
}
|
}
|
||||||
|
if p.GetStatusCode() != 404 {
|
||||||
|
t.Errorf("powered status_code = %d, want 404", p.GetStatusCode())
|
||||||
|
}
|
||||||
|
metadata := p.GetMetadata()
|
||||||
|
if metadata == nil || metadata.GetTitle() != "Missing greeting" ||
|
||||||
|
metadata.GetDescription() != "The greeting was not found." ||
|
||||||
|
metadata.GetCanonicalUrl() != "https://example.com/greetings/missing" ||
|
||||||
|
metadata.GetImageUrl() != "https://example.com/greetings/missing.png" ||
|
||||||
|
metadata.GetRobots() != "noindex,nofollow" {
|
||||||
|
t.Errorf("powered metadata = %+v", metadata)
|
||||||
|
}
|
||||||
|
|
||||||
// HOOK_RENDER_TAG dispatches to the registered tag fn (fresh invoke —
|
// HOOK_RENDER_TAG dispatches to the registered tag fn (fresh invoke —
|
||||||
// no re-entrancy; RENDER_BLOCK already returned).
|
// no re-entrancy; RENDER_BLOCK already returned).
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
|
||||||
@ -350,7 +351,7 @@ func renderBlock(ctx context.Context, block map[string]any) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
img := fmt.Sprintf(`<img src="%s" alt="%s" />`, html.EscapeString(url), html.EscapeString(alt))
|
img := fmt.Sprintf(`<img src="%s" alt="%s" />`, html.EscapeString(url), html.EscapeString(alt))
|
||||||
if link != "" {
|
if link = safeLinkURL(link); link != "" {
|
||||||
img = fmt.Sprintf(`<a href="%s">%s</a>`, html.EscapeString(link), img)
|
img = fmt.Sprintf(`<a href="%s">%s</a>`, html.EscapeString(link), img)
|
||||||
}
|
}
|
||||||
// The positioning wrapper only exists when a chip is rendered, so
|
// The positioning wrapper only exists when a chip is rendered, so
|
||||||
@ -424,8 +425,8 @@ func renderBlock(ctx context.Context, block map[string]any) string {
|
|||||||
name = url
|
name = url
|
||||||
}
|
}
|
||||||
sb.WriteString(`<div class="my-4 rounded border border-border p-4">`)
|
sb.WriteString(`<div class="my-4 rounded border border-border p-4">`)
|
||||||
if url != "" {
|
if href := safeLinkURL(url); href != "" {
|
||||||
fmt.Fprintf(&sb, `<a class="text-primary underline" href="%s">`, html.EscapeString(url))
|
fmt.Fprintf(&sb, `<a class="text-primary underline" href="%s">`, html.EscapeString(href))
|
||||||
sb.WriteString(html.EscapeString(name))
|
sb.WriteString(html.EscapeString(name))
|
||||||
sb.WriteString("</a>")
|
sb.WriteString("</a>")
|
||||||
} else {
|
} else {
|
||||||
@ -627,6 +628,7 @@ func renderInlineContent(content []map[string]any, insideLink bool) string {
|
|||||||
|
|
||||||
case "link":
|
case "link":
|
||||||
href, _ := itemMap["href"].(string)
|
href, _ := itemMap["href"].(string)
|
||||||
|
href = safeLinkURL(href)
|
||||||
linkContent := inlineContentFromRaw(itemMap["content"])
|
linkContent := inlineContentFromRaw(itemMap["content"])
|
||||||
if href == "" {
|
if href == "" {
|
||||||
sb.WriteString(renderInlineContent(linkContent, insideLink))
|
sb.WriteString(renderInlineContent(linkContent, insideLink))
|
||||||
@ -656,6 +658,30 @@ func renderInlineContent(content []map[string]any, insideLink bool) string {
|
|||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// safeLinkURL accepts ordinary web, email, telephone, and relative links while
|
||||||
|
// rejecting active-content schemes such as javascript: and data:. Escaping a
|
||||||
|
// URL protects the HTML attribute boundary, but it does not make an unsafe URL
|
||||||
|
// scheme safe to navigate to.
|
||||||
|
func safeLinkURL(raw string) string {
|
||||||
|
trimmed := strings.TrimSpace(raw)
|
||||||
|
if trimmed == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
parsed, err := url.Parse(trimmed)
|
||||||
|
if err != nil || parsed.Scheme == "" {
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
switch strings.ToLower(parsed.Scheme) {
|
||||||
|
case "http", "https", "mailto", "tel":
|
||||||
|
return trimmed
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// imageChipClass matches the page-builder image block's attribution chip
|
// imageChipClass matches the page-builder image block's attribution chip
|
||||||
// (backend/blocks/tags via image.ninjatpl in the cms repo) so themes style
|
// (backend/blocks/tags via image.ninjatpl in the cms repo) so themes style
|
||||||
// blog and page credits identically.
|
// blog and page credits identically.
|
||||||
@ -700,8 +726,9 @@ func writeAttributionLink(b *strings.Builder, href, label string) {
|
|||||||
// Trailing sentence punctuation (.,;:!?'") is excluded from the linked URL.
|
// Trailing sentence punctuation (.,;:!?'") is excluded from the linked URL.
|
||||||
// Closing parens, brackets and braces are kept inside the URL only when
|
// Closing parens, brackets and braces are kept inside the URL only when
|
||||||
// balanced with an opener inside the URL itself — so
|
// balanced with an opener inside the URL itself — so
|
||||||
// "(see https://example.com)" links only "https://example.com"
|
//
|
||||||
// "https://en.wikipedia.org/wiki/Foo_(bar)" keeps the trailing paren.
|
// "(see https://example.com)" links only "https://example.com"
|
||||||
|
// "https://en.wikipedia.org/wiki/Foo_(bar)" keeps the trailing paren.
|
||||||
func autolinkText(text string) string {
|
func autolinkText(text string) string {
|
||||||
const scheme = "https://"
|
const scheme = "https://"
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
|
|||||||
@ -183,6 +183,27 @@ func TestBlockNoteToHTML_NoNestedAnchorInsideExplicitLink(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockNoteToHTML_RejectsActiveContentExplicitLink(t *testing.T) {
|
||||||
|
doc := map[string]any{
|
||||||
|
"blocks": []any{
|
||||||
|
map[string]any{
|
||||||
|
"type": "paragraph",
|
||||||
|
"content": []any{
|
||||||
|
map[string]any{
|
||||||
|
"type": "link",
|
||||||
|
"href": " javascript:alert(1) ",
|
||||||
|
"content": []any{map[string]any{"type": "text", "text": "read this"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
html := BlockNoteToHTML(context.Background(), doc)
|
||||||
|
if html != "<p class=\"my-4\">read this</p>\n" {
|
||||||
|
t.Fatalf("unsafe link should render as plain text: %q", html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBlockNoteToHTML_NoAutolinkInsideCodeStyle(t *testing.T) {
|
func TestBlockNoteToHTML_NoAutolinkInsideCodeStyle(t *testing.T) {
|
||||||
doc := map[string]any{
|
doc := map[string]any{
|
||||||
"blocks": []any{
|
"blocks": []any{
|
||||||
|
|||||||
@ -236,6 +236,28 @@ func TestFileBlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFileBlockRejectsActiveContentURL(t *testing.T) {
|
||||||
|
doc := map[string]any{
|
||||||
|
"blocks": []any{
|
||||||
|
map[string]any{
|
||||||
|
"type": "file",
|
||||||
|
"props": map[string]any{
|
||||||
|
"url": "javascript:alert(document.domain)",
|
||||||
|
"name": "Unsafe link",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
html := BlockNoteToHTML(context.Background(), doc)
|
||||||
|
|
||||||
|
if strings.Contains(html, "href=") || strings.Contains(html, "javascript:") {
|
||||||
|
t.Fatalf("unsafe file URL rendered as a link: %s", html)
|
||||||
|
}
|
||||||
|
if !strings.Contains(html, "Unsafe link") {
|
||||||
|
t.Fatalf("file label should remain visible as plain text: %s", html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFileBlockNoName(t *testing.T) {
|
func TestFileBlockNoName(t *testing.T) {
|
||||||
doc := map[string]any{
|
doc := map[string]any{
|
||||||
"blocks": []any{
|
"blocks": []any{
|
||||||
@ -708,6 +730,18 @@ func TestImageBlockAltAndLink(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestImageBlockRejectsActiveContentLink(t *testing.T) {
|
||||||
|
html := BlockNoteToHTML(context.Background(), imageBlock(map[string]any{
|
||||||
|
"url": "/media/x.webp", "alt": "Sunset", "link": "JaVaScRiPt:alert(1)",
|
||||||
|
}))
|
||||||
|
if strings.Contains(html, "<a ") || strings.Contains(strings.ToLower(html), "javascript:") {
|
||||||
|
t.Fatalf("unsafe image link rendered: %s", html)
|
||||||
|
}
|
||||||
|
if !strings.Contains(html, `<img src="/media/x.webp"`) {
|
||||||
|
t.Fatalf("image should still render without its unsafe link: %s", html)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestImageBlockWidthAndAlign(t *testing.T) {
|
func TestImageBlockWidthAndAlign(t *testing.T) {
|
||||||
html := BlockNoteToHTML(context.Background(), imageBlock(map[string]any{
|
html := BlockNoteToHTML(context.Background(), imageBlock(map[string]any{
|
||||||
"url": "/media/x.webp", "width": "medium", "align": "right",
|
"url": "/media/x.webp", "width": "medium", "align": "right",
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package templates
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"git.dev.alexdunmow.com/block/pluginsdk/document"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PageDocument is a full-page template's contribution to the document. The
|
// PageDocument is a full-page template's contribution to the document. The
|
||||||
@ -17,6 +19,8 @@ type PageDocument struct {
|
|||||||
HTMLAttrs map[string]string // extra <html> attributes (e.g. data-theme)
|
HTMLAttrs map[string]string // extra <html> attributes (e.g. data-theme)
|
||||||
HeadExtra HTMLComponent // appended inside <head> after bn.Head
|
HeadExtra HTMLComponent // appended inside <head> after bn.Head
|
||||||
BodyEndExtra HTMLComponent // appended before </body> after bn.BodyEnd
|
BodyEndExtra HTMLComponent // appended before </body> after bn.BodyEnd
|
||||||
|
StatusCode int32 // requested HTTP status; host accepts 200–599
|
||||||
|
Metadata *document.Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PageDocument) Render(ctx context.Context, w io.Writer) error {
|
func (p *PageDocument) Render(ctx context.Context, w io.Writer) error {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user