pluginsdk/plugin/bridge.go

63 lines
2.4 KiB
Go

package plugin
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.
// Plugins register named services during startup; other plugins look them up at runtime.
//
// In-process (bundled) plugins can share typed Go values via
// RegisterService/GetService. Across the wasm sandbox a typed value cannot
// cross, so cross-plugin calls go through Invoke instead: the provider's
// service implements BridgeInvokable and the consumer calls Invoke with an
// opaque payload (JSON by convention). GetService still reports availability
// only (nil) for wasm consumers.
type PluginBridge interface {
RegisterService(pluginName, serviceName string, service any)
GetService(pluginName, serviceName string) any
// Invoke calls a method on another plugin's registered bridge service.
// The provider answers via its BridgeInvokable implementation (bundled)
// or the BRIDGE_CALL hook (wasm).
Invoke(ctx context.Context, pluginName, serviceName, method string, payload []byte) ([]byte, error)
}
// BridgeInvokable is implemented by bridge service values that support
// method-by-name invocation with opaque payloads, making them callable from
// other plugins across the wasm sandbox boundary.
type BridgeInvokable interface {
InvokeBridge(ctx context.Context, method string, payload []byte) ([]byte, error)
}
// GetServiceAs retrieves a typed service from the bridge.
func GetServiceAs[T any](bridge PluginBridge, pluginName, serviceName string) (T, bool) {
var zero T
if bridge == nil {
return zero, false
}
svc := bridge.GetService(pluginName, serviceName)
if svc == nil {
return zero, false
}
typed, ok := svc.(T)
return typed, ok
}