pluginsdk/plugin/wasmguest/registries.go
Alex Dunmow b6d40ed8ac feat: bootstrap block/pluginsdk — the proto-first plugin SDK (P1)
Bootstrap the plugin-facing SDK module so the fleet migration (P2) becomes a
mechanical block/core/X -> block/pluginsdk/X import rewrite.

- abi/proto/v1: the single source-of-truth ABI proto tree, copied from the cms
  authoring source (cms/backend/abi/proto/v1) with go_package retargeted to
  git.dev.alexdunmow.com/block/pluginsdk/abi/v1;abiv1. Kills the hand-kept
  cms/core proto duplication (audit gap 4).
- abi/v1: generated Go bindings (buf generate); byte-identical to core's abi/v1
  save the embedded go_package path.
- plugin/ (registration + DI surface), plugin/wasmguest/** (transport shim,
  caps stubs, bnwasm db driver, testdata fixtures + golden .pb), and the
  guest-facing type packages: blocks (+builtin/shared/tags), templates
  (+pongo/bn), auth, settings, content, gating, crypto, rbac, video, ai,
  subscriptions, menus, datasources. Internal imports rewritten core ->
  pluginsdk; zero block/core references remain.
- README/AGENTS(+CLAUDE symlink)/Makefile: proto is the contract, Go is one
  binding; no replace directives; templates/bn is a synced copy authored in cms.

Spec: cms docs/superpowers/specs/2026-07-07-proto-first-plugin-sdk-design.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:51:00 +08:00

115 lines
3.9 KiB
Go

package wasmguest
import (
"io/fs"
"git.dev.alexdunmow.com/block/pluginsdk/blocks"
"git.dev.alexdunmow.com/block/pluginsdk/templates"
)
// captureBlockRegistry implements blocks.BlockRegistry by recording every
// registration instead of wiring it into a live host. It backs both DESCRIBE
// (metadata enumeration) and runtime dispatch (BlockFunc lookup).
//
// It is always used behind plugin.NewPluginBlockRegistry, so keys arrive
// already plugin-prefixed and overrides arrive with their source set —
// identical to what a plugin sees in the .so world.
type captureBlockRegistry struct {
metas []blocks.BlockMeta
funcs map[string]blocks.BlockFunc // block key → fn
overrides []blockOverride
}
type blockOverride struct {
templateKey string
blockKey string
source string
fn blocks.BlockFunc
}
func newCaptureBlockRegistry() *captureBlockRegistry {
return &captureBlockRegistry{funcs: make(map[string]blocks.BlockFunc)}
}
func (r *captureBlockRegistry) Register(meta blocks.BlockMeta, fn blocks.BlockFunc) {
r.metas = append(r.metas, meta)
r.funcs[meta.Key] = fn
}
func (r *captureBlockRegistry) RegisterTemplateOverride(templateKey, blockKey string, fn blocks.BlockFunc) {
r.overrides = append(r.overrides, blockOverride{templateKey: templateKey, blockKey: blockKey, fn: fn})
}
func (r *captureBlockRegistry) RegisterTemplateOverrideWithSource(templateKey, blockKey, source string, fn blocks.BlockFunc) {
r.overrides = append(r.overrides, blockOverride{templateKey: templateKey, blockKey: blockKey, source: source, fn: fn})
}
// LoadSchemasFromFS is a no-op in the wasm guest: block schemas ship in the
// .bnp artifact's schemas/ directory and are loaded host-side.
func (r *captureBlockRegistry) LoadSchemasFromFS(fs.FS) error { return nil }
func (r *captureBlockRegistry) LoadSchemasFromFSWithPrefix(fs.FS, string) error { return nil }
// lookup resolves a block function for dispatch: a template-scoped override
// wins over the base registration, mirroring the host registry's behavior.
func (r *captureBlockRegistry) lookup(blockKey, templateKey string) (blocks.BlockFunc, bool) {
if templateKey != "" {
for _, o := range r.overrides {
if o.templateKey == templateKey && o.blockKey == blockKey {
return o.fn, true
}
}
}
fn, ok := r.funcs[blockKey]
return fn, ok
}
// captureTemplateRegistry implements templates.TemplateRegistry by recording
// registrations for DESCRIBE and runtime dispatch.
type captureTemplateRegistry struct {
templateKeys []string // insertion order of Register calls
funcs map[string]templates.TemplateFunc
systemTemplates []templates.SystemTemplateMeta
pageTemplates []capturedPageTemplate
emailWrappers []capturedEmailWrapper
}
type capturedPageTemplate struct {
systemKey string
meta templates.PageTemplateMeta
}
type capturedEmailWrapper struct {
systemKey string
fn templates.EmailWrapperFunc
}
func newCaptureTemplateRegistry() *captureTemplateRegistry {
return &captureTemplateRegistry{funcs: make(map[string]templates.TemplateFunc)}
}
func (r *captureTemplateRegistry) Register(key string, fn templates.TemplateFunc) error {
r.templateKeys = append(r.templateKeys, key)
r.funcs[key] = fn
return nil
}
func (r *captureTemplateRegistry) RegisterSystemTemplate(meta templates.SystemTemplateMeta) {
r.systemTemplates = append(r.systemTemplates, meta)
}
func (r *captureTemplateRegistry) RegisterPageTemplate(systemKey string, meta templates.PageTemplateMeta, fn templates.TemplateFunc) error {
r.pageTemplates = append(r.pageTemplates, capturedPageTemplate{systemKey: systemKey, meta: meta})
r.funcs[meta.Key] = fn
return nil
}
func (r *captureTemplateRegistry) RegisterEmailWrapper(systemKey string, fn templates.EmailWrapperFunc) {
r.emailWrappers = append(r.emailWrappers, capturedEmailWrapper{systemKey: systemKey, fn: fn})
}
func (r *captureTemplateRegistry) lookup(key string) (templates.TemplateFunc, bool) {
fn, ok := r.funcs[key]
return fn, ok
}