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 }