feat(plugin): declare admin API compatibility

This commit is contained in:
Alex Dunmow 2026-08-19 23:21:47 +08:00
parent abef5b70da
commit 731d6b51d1
3 changed files with 47 additions and 2 deletions

View File

@ -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

View File

@ -91,6 +91,7 @@ type ModPublicRoute struct {
type ModCompat struct { type ModCompat struct {
BlockCore string `toml:"block_core"` BlockCore string `toml:"block_core"`
AdminAPI string `toml:"admin_api"`
} }
type ModRequirement struct { type ModRequirement struct {

View File

@ -278,6 +278,7 @@ 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"
[[requires]] [[requires]]
name = "@blockninja/smartblock" name = "@blockninja/smartblock"
@ -291,8 +292,14 @@ 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 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))