Plugins
Glubean has two distinct plugin concepts. Pick the one that matches what you’re extending.
| Concept | API | Scope | Consumed by |
|---|---|---|---|
| Plugin manifest (global) | definePlugin(manifest) + installPlugin(manifest) | Process-wide matchers, protocol adapters, one-time setup | glubean.setup.ts |
| Client factory (per file) | defineClientFactory((runtime) => client) | Per-test lazy client instance (shared auth, HTTP, domain helpers) | configure({ plugins }) |
The legacy definePlugin((runtime) => T) overload has been removed. Use defineClientFactory for that use case.
1. Client factory — per-file shared client
Use this when multiple tests in the same file need the same client (e.g. an internal API wrapper that reads a base URL from vars).
Basic pattern
- Define a factory with
defineClientFactory(...) - Register it in
configure({ plugins }) - Use the shared instance in tests
The client is created lazily on first access during test execution.
Example
import { configure, defineClientFactory, test } from "@glubean/sdk";
const myClient = (opts: { endpointKey: string }) =>
defineClientFactory((runtime) => {
const baseUrl = runtime.requireVar(opts.endpointKey);
return {
getStatus: async () => runtime.http.get(`${baseUrl}/status`).json(),
};
});
const { api } = configure({
plugins: {
api: myClient({ endpointKey: "INTERNAL_API_URL" }),
},
});
export const health = test("internal-health", async (ctx) => {
const status = await api.getStatus();
ctx.expect(status.ok).toBe(true);
});Runtime object passed to the factory
| Property | Description |
|---|---|
runtime.http | HTTP client (auto-traced) |
runtime.requireVar(key) | Read a var (throws if missing) |
runtime.requireSecret(key) | Read a secret (throws if missing) |
runtime.resolveTemplate(str) | Resolve {{KEY}} placeholders |
runtime.vars | Raw vars record |
runtime.secrets | Raw secrets record |
runtime.action(a) | Emit a custom action event |
runtime.event(ev) | Emit a custom event |
runtime.log(msg, data?) | Log from plugin context |
Notes
- Reserved keys (
vars,secrets,http) cannot be used as plugin names inconfigure({ plugins }). - Clients are cached per test execution and recreated for each new test.
2. Plugin manifest — global matchers / protocol adapters
Use this when you want to publish an npm package that adds process-wide behavior: custom ctx.expect(...) matchers, a new contract protocol (e.g. contract.graphql), or a one-time setup() hook that runs before any tests load.
This is what @glubean/graphql, @glubean/grpc, @glubean/auth, and @glubean/browser are.
Authoring a manifest
// packages/my-plugin/src/index.ts
import { contract, definePlugin } from "@glubean/sdk";
import type { PluginManifest } from "@glubean/sdk";
const myPlugin: PluginManifest = definePlugin({
name: "@my-scope/my-plugin",
matchers: {
toBeMyThing(actual) {
return { pass: actual === "thing", message: () => "..." };
},
},
contracts: {
myproto: myprotoAdapter,
},
setup() {
// Optional. Runs once after matchers/contracts are registered.
},
});
export default myPlugin;Installing it in a project — required
A plugin manifest has no effect until installPlugin(manifest) runs. The canonical place to call it is glubean.setup.ts at the project root:
// glubean.setup.ts (project root, alongside ci-config/ and tests/)
import { installPlugin } from "@glubean/sdk";
import graphqlPlugin from "@glubean/graphql";
import grpcPlugin from "@glubean/grpc";
await installPlugin(graphqlPlugin, grpcPlugin);glubean.setup.ts runs exactly once per process (CLI run, CLI contracts, MCP tool calls, VSCode scan) before any test file or .contract.ts module is imported. Matchers and protocol adapters are only available after this file executes.
A top-level import "@glubean/graphql" is not a side-effect registration anymore — you must explicitly installPlugin.
Install-time guarantees
- Installing the same plugin twice (same
name) is a no-op. - Duplicate matcher or protocol names across plugins throw at install time.
- If
setup()throws, the plugin is marked failed and retrying in the same process throws with a clear error — restart to recover.
First-party plugin manifests
- Auth (
@glubean/auth) — OAuth, API key, and custom auth flows - Browser (
@glubean/browser) — Puppeteer-powered browser automation - GraphQL (
@glubean/graphql) — Typed GraphQL queries, mutations, and contract protocol - gRPC (
@glubean/grpc) — gRPC status matchers and contract protocol
All of these require await installPlugin(...) in glubean.setup.ts.