plugin

package
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package plugin 是插件宿主:编译、加载、按钩子调用本地 JavaScript 插件,并提供 校验与回放工具。

插件是单文件同步 ECMAScript 模块,导出 meta 与若干钩子函数。契约见 plugin-api/v1/v1.d.ts 与 docs/plugin.md。

安全边界:宿主不提供网络、文件系统、require、import 与动态代码执行;插件的执行 时长、并发数、源码大小与单次输入大小都有限额。加载插件等同于管理员级信任决定。

Index

Constants

View Source
const (
	// SeverityError 表示插件不可用。
	SeverityError = "error"
	// SeverityWarning 表示插件可用但值得注意。
	SeverityWarning = "warning"
)

检查结论的级别。

View Source
const (
	DefaultSourceBytes  = 1 << 20
	DefaultPayloadBytes = 8 << 20
	DefaultTimeout      = 50 * time.Millisecond
	DefaultConcurrency  = 8
)

默认限额。

View Source
const APIVersion = "v1"

APIVersion 是当前支持的插件契约版本。

View Source
const (
	PluginFile = "plugin.js"
)

插件文件名。目录形式为 <key>/plugin.js,也接受平铺的 <key>.js。

Variables

View Source
var (
	// ErrPayloadTooLarge 表示单次输入或输出超过限额。
	ErrPayloadTooLarge = errors.New("plugin: 负载超过限额")
	// ErrAsyncHook 表示钩子返回了 Promise。钩子必须是同步函数。
	ErrAsyncHook = errors.New("plugin: 钩子必须是同步函数,不能返回 Promise")
	// ErrMissingMeta 表示插件没有导出 meta。
	ErrMissingMeta = errors.New("plugin: 缺少 meta 导出")
	// ErrHookMissing 表示插件没有导出调用方要求的钩子。
	ErrHookMissing = errors.New("plugin: 插件未导出该钩子")
)

错误集合。

Functions

func ValidateFixture

func ValidateFixture(fixture Fixture) error

ValidateFixture 校验 fixture 的结构。

Types

type CaseResult

type CaseResult struct {
	Name    string `json:"name"`
	Passed  bool   `json:"passed"`
	Message string `json:"message,omitempty"`
}

CaseResult 是一条用例的结果。

type Entry

type Entry struct {
	// Instance 是绑定配置后的插件。
	Instance *Instance
	// Enabled 是启用状态,取自配置。
	Enabled bool
	// Order 是执行顺序,越小越先执行。
	Order int
	// Position 是加载顺序,Order 相同时按它排序。
	Position int
}

Entry 是插件集合里的一项。

func (Entry) Key

func (e Entry) Key() string

Key 返回插件 key。

type Failure

type Failure struct {
	// Key 是插件 key。
	Key string
	// Hook 是钩子名。
	Hook Hook
	// Err 是失败原因。
	Err error
}

Failure 是一次失败的钩子调用。钩子失败不影响转发可用性,只记录事实。

func (Failure) Error

func (f Failure) Error() string

Error 实现 error。

func (Failure) Unwrap

func (f Failure) Unwrap() error

Unwrap 返回底层错误。

type Fixture

type Fixture struct {
	// UnixNow 固定插件内 host.now() 的取值,让签名与时间戳类钩子可复现。
	// 为 0 时用当前时间。
	UnixNow int64 `json:"unixNow"`
	// Cases 是用例列表。
	Cases []FixtureCase `json:"cases"`
}

Fixture 是一组插件回放用例。形状与 new-api 的任务插件 fixture 一致,便于互相 搬运用例。

func LoadFixture

func LoadFixture(path string) (Fixture, error)

LoadFixture 读取 fixture 文件。

type FixtureCase

type FixtureCase struct {
	// Name 是用例名。
	Name string `json:"name"`
	// Hook 是要调用的钩子。
	Hook Hook `json:"hook"`
	// Member 指定只比较返回值的某个成员,与 Path 互斥。
	Member string `json:"member,omitempty"`
	// Path 是按点分路径定位返回值的比较目标,与 Member 互斥。数字段表示数组下标。
	Path string `json:"path,omitempty"`
	// Args 是钩子的参数列表,第一个参数即钩子的输入负载。
	Args []json.RawMessage `json:"args"`
	// Expected 是期望值,与 ExpectedError 互斥。
	Expected json.RawMessage `json:"expected,omitempty"`
	// ExpectedError 是期望的错误信息子串,与 Expected 互斥。
	ExpectedError string `json:"expectedError,omitempty"`
}

FixtureCase 是一条回放用例。

type Hook

type Hook string

Hook 是钩子名,即插件要导出的函数名。

const (
	// HookRequest 在选路与转发之前改写请求。
	HookRequest Hook = "onRequest"
	// HookEvent 在每条流式事件写回客户端之前改写它。
	HookEvent Hook = "onEvent"
	// HookStreamEnd 在流结束前改写补发的分片。
	HookStreamEnd Hook = "onStreamEnd"
	// HookResponse 在非流式响应写回客户端之前改写它。
	HookResponse Hook = "onResponse"
)

func Hooks

func Hooks() []Hook

Hooks 按生命周期顺序返回四个钩子。

func (Hook) Valid

func (h Hook) Valid() bool

Valid 报告是否是已知钩子。

type Instance

type Instance struct {
	// contains filtered or unexported fields
}

Instance 是绑定配置后的插件。可以并发调用。

func (*Instance) Call

func (i *Instance) Call(ctx context.Context, hook Hook, input []byte, session Session) ([]byte, bool, error)

Call 调用一个钩子。

input 与返回的 out 都是 JSON。第二个返回值报告插件是否给出了新值:返回 null 或 undefined 表示不改写,此时 out 等于 input。

调用超时会中断插件,超时与异常都按错误返回,调用方按「改写失效、原样转发」 处理。

func (*Instance) Config

func (i *Instance) Config() map[string]any

Config 返回绑定到实例上的配置。

func (*Instance) Has

func (i *Instance) Has(hook Hook) bool

Has 报告插件是否导出了该钩子。

func (*Instance) Key

func (i *Instance) Key() string

Key 返回插件 key。

func (*Instance) Manifest

func (i *Instance) Manifest() Manifest

Manifest 返回自述信息。

func (*Instance) Path

func (i *Instance) Path() string

Path 返回插件文件路径。

func (*Instance) Replay

func (i *Instance) Replay(fixture Fixture, logger *slog.Logger) []CaseResult

Replay 回放 fixture 里的全部用例。

钩子返回 null 或 undefined 表示不改写,此时实际值按 null 比较 —— 这与转发路径 上「不改写就用原值」的语义不同,回放关心的是插件自己的返回值。

type Issue

type Issue struct {
	// Path 是插件文件路径。
	Path string `json:"path"`
	// Severity 取 error 或 warning。
	Severity string `json:"severity"`
	// Message 是结论说明。
	Message string `json:"message"`
}

Issue 是一条检查结论。

func LintFile

func LintFile(path string, limits Limits) ([]Issue, error)

LintFile 读取并检查一个插件文件。返回的 error 只表示读文件失败。

func LintSource

func LintSource(path string, source []byte, limits Limits) []Issue

LintSource 检查插件源码,返回全部结论。

func (Issue) IsError

func (i Issue) IsError() bool

IsError 报告这条结论是否致命。

func (Issue) String

func (i Issue) String() string

String 返回可读的一行。

type Limits

type Limits struct {
	// SourceBytes 是单个插件源码的字节上限。
	SourceBytes int
	// PayloadBytes 是单次钩子调用输入与输出的字节上限。
	PayloadBytes int
	// Timeout 是单次钩子调用的上限。
	Timeout time.Duration
	// Concurrency 是同一插件同时执行的钩子上限。
	Concurrency int
}

Limits 是插件执行的限额。零值不可用,请用 DefaultLimits。

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits 返回默认限额。默认超时取 50 毫秒:本地改写的正常耗时在微秒级, 超过这个量级的插件应当在转发路径之外完成。

type Loaded

type Loaded struct {
	// contains filtered or unexported fields
}

Loaded 是编译好的插件。编译一次,可被任意多个 Instance 复用,可并发使用。

func Compile

func Compile(path string, source []byte, limits Limits) (*Loaded, error)

Compile 编译插件源码并读出它的自述信息与钩子。

path 只用于报错与展示。key 取自 meta.key。

func LoadDir

func LoadDir(dir string, limits Limits) ([]*Loaded, error)

LoadDir 扫描插件目录,按文件名排序返回全部插件。

目录里每一项要么是子目录,要么是 .js 文件;两种形式都要求名字与 meta.key 一致。 其它文件与目录会被忽略,因此插件目录里可以放图标与说明。

func LoadFile

func LoadFile(path string, limits Limits) (*Loaded, error)

LoadFile 读取并编译一个插件文件。

func (*Loaded) Bind

func (l *Loaded) Bind(config map[string]any) *Instance

Bind 把一份配置绑到插件上,得到可调用的实例。

func (*Loaded) Key

func (l *Loaded) Key() string

Key 返回插件 key。

func (*Loaded) Limits

func (l *Loaded) Limits() Limits

Limits 返回限额。

func (*Loaded) Manifest

func (l *Loaded) Manifest() Manifest

Manifest 返回自述信息的副本。

func (*Loaded) Path

func (l *Loaded) Path() string

Path 返回插件文件路径。

type Manifest

type Manifest struct {
	// Key 是插件标识,须与目录名一致。
	Key string `json:"key"`
	// Name 是展示名。
	Name string `json:"name"`
	// Version 是插件自己的版本号。
	Version string `json:"version"`
	// APIVersion 是插件声明的契约版本,目前只支持 v1。
	APIVersion string `json:"apiVersion"`
	// Description 是可选说明。
	Description string `json:"description,omitempty"`
	// Hooks 是实际导出的钩子,由宿主在编译后填入,插件不必自己写。
	Hooks []Hook `json:"hooks,omitempty"`
}

Manifest 是插件的自述信息,取自模块导出的 meta 对象。

func (Manifest) Has

func (m Manifest) Has(hook Hook) bool

Has 报告插件是否导出了该钩子。

func (Manifest) Validate

func (m Manifest) Validate() error

Validate 校验自述信息。key 与目录名的比对由加载器完成。

type Merger

type Merger func(payload, rewrite []byte) ([]byte, error)

Merger 把插件的改写结果合并回负载。

payload 是合并前的负载,rewrite 是插件返回的新值。返回 nil 表示本次不改写, 返回错误按插件失败处理。

type Outcome

type Outcome struct {
	// Data 是链路走完后的负载。
	Data []byte
	// Applied 是实际改写了负载的插件 key,按执行顺序。
	Applied []string
	// Failures 是执行失败的插件。
	Failures []Failure
}

Outcome 是一次钩子链调用的结果。

func (Outcome) Changed

func (o Outcome) Changed() bool

Changed 报告是否有插件改写过负载。

type Session

type Session struct {
	// Now 是插件内 host.now() 看到的时间。
	Now time.Time
	// Logger 接收插件通过 host.log 写出的日志,为 nil 时丢弃。
	Logger *slog.Logger
}

Session 是一次调用携带的宿主环境。

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store 是插件集合。可以并发使用。

func EmptyStore

func EmptyStore(logger *slog.Logger) *Store

EmptyStore 返回一个不含插件的集合。

func NewStore

func NewStore(entries []Entry, logger *slog.Logger) (*Store, error)

NewStore 创建插件集合。空集合是合法状态:没有任何插件时所有钩子都直通。

func (*Store) Apply

func (s *Store) Apply(ctx context.Context, hook Hook, input []byte, merger Merger, keys []string, session Session) Outcome

Apply 按顺序对启用插件调用一个钩子,每个插件看到的是上一个插件改写之后的完整 负载,而不是只看到上一个插件的返回值。

keys 为空表示全部启用插件参与;非空时只有列出的 key 参与。 merger 为 nil 时,插件返回的新值直接成为下一次调用的负载。

单个插件失败不影响链路:该插件被跳过,前一个成功的改写结果继续传给后面的插件。

func (*Store) Entries

func (s *Store) Entries() []Entry

Entries 按执行顺序返回全部插件。

func (*Store) Get

func (s *Store) Get(key string) (*Instance, bool)

Get 按 key 取插件实例。

func (*Store) HasAny

func (s *Store) HasAny(hook Hook) bool

HasAny 报告是否有启用插件导出了该钩子。没有插件参与时,调用方可以走不解析 负载的快速路径。

func (*Store) Keys

func (s *Store) Keys() []string

Keys 按执行顺序返回全部 key。

func (*Store) Len

func (s *Store) Len() int

Len 返回插件数量。

func (*Store) Missing

func (s *Store) Missing(keys []string) []string

Missing 返回给了名字但集合里没有的 key,供配置校验报告。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL