Skip to Content

Plugins

Glubean has two distinct plugin concepts. Pick the one that matches what you’re extending.

ConceptAPIScopeConsumed by
Plugin manifest (global)definePlugin(manifest) + installPlugin(manifest)Process-wide matchers, protocol adapters, one-time setupglubean.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

  1. Define a factory with defineClientFactory(...)
  2. Register it in configure({ plugins })
  3. 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

PropertyDescription
runtime.httpHTTP 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.varsRaw vars record
runtime.secretsRaw 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 in configure({ 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

All of these require await installPlugin(...) in glubean.setup.ts.

Last updated on