@buddy-works/docs-schema

中文文档
0.1.4发布于 2026/11/05MITTypeScriptESM + CJS近 30 天下载 2804

Buddy's YAML and REST API schemas, fetched, cached and resolved into documented shapes

原始使用文档
文档目录15 节
  1. Installation
  2. Usage
  3. How it gets the schemas
  4. Supplying the documents yourself
  5. Methods
  6. Every YAML scope
  7. Scopes with a root
  8. Scopes with a union
  9. pipelines
  10. api
  11. On the client
  12. Options
  13. Errors
  14. Validating a YAML file
  15. Requirements

@buddy-works/docs-schema

Fetches Buddy's YAML and REST API schemas and resolves them into shapes you can render or search. It follows $ref pointers, flattens allOf, and keeps enums, constraints and examples.

Installation

npm install @buddy-works/docs-schema

Usage

import { BuddySchema } from "@buddy-works/docs-schema";

const parser = await BuddySchema.load();

await parser.actions.list();                  // every action a pipeline can run
await parser.actions.get("AMAZON_S3");        // one action, in full
await parser.actions.search("notify");        // the ones that send notifications
await parser.search("deploy to s3");          // across YAML and the REST API
await parser.api.endpoint("GET", "/workspaces/{workspace_domain}/projects");

How it gets the schemas

BuddySchema.load() reads from a local cache and revalidates over HTTP with an ETag once the cache is 3 hours old. A 304 keeps the cached copy and starts the window again. A 200 replaces the document. Nothing is fetched until a call needs it, so a process that only asks about actions never downloads the OpenAPI document.

In a long-running process such as an MCP server, the check runs in the background and the call answers from memory. A query never waits on the network. When a 200 lands, the new document takes over and the shaped results derived from the old one are dropped. A query already in flight finishes against the graph it started with.

A response that does not parse, or that lacks the key its scope depends on, is refused. The previous good copy stays in place rather than being overwritten by an error page. If the server cannot be reached at all, the cached copy keeps being served and stats() reports staleSince for that document.

It reads production unless environment says otherwise. The other names are "stage", "master" and "dev". Each one caches separately, so a process can hold two at once:

const live = await BuddySchema.load();
const stage = await BuddySchema.load({ environment: "stage" });

Supplying the documents yourself

If you already have the schemas, hand them over instead of letting the package fetch. This covers a pinned version, a file on disk, or a schema that is not published. Nothing is fetched and nothing is cached:

import { readFileSync } from "node:fs";

const parser = BuddySchema.from({
  pipelines: JSON.parse(readFileSync("yaml-schema.json", "utf8")),
});

The keys are documents, not scopes, and pipelines backs two of them:

KeyScopes it serves
pipelinesparser.pipelines, parser.actions
targetsparser.targets
environmentsparser.environments
sandboxesparser.sandboxes
agentsparser.agents
domainsparser.domains
customActionsparser.customActions
distributionsparser.distributions
apiparser.api

Reaching a scope whose document you did not supply throws and names what was given.

Methods

Ten scopes over those nine documents. What a scope offers depends on what its schema declares, and the types enforce it. parser.agents.list() does not compile, because the agents schema has no union.

Every YAML scope

MethodWhat it does
names(options?)Definition names in the document, minus the ones the schema retired. { includeDeprecated: true } lists those too.
await parser.pipelines.names() → ["PipelineYaml", "TransferActionYaml", …]
def(name)One definition as a document: fields, types it refers to, examples.
await parser.pipelines.def("VariableYaml")
properties(name)The same definition flattened into property rows, the shape a table renders. Empty for a name the document does not declare.
await parser.pipelines.properties("VariableYaml")
search(query, options?)Ranked hits. The query is split into words and results rank by how many they match, so a whole question works.
await parser.actions.search("notify") → ROLLBAR, SMS, HONEYBADGER…
await parser.search("deploy to s3") → AMAZON_S3…
search(query, { fuzzy: true })The same, tolerating typos. Off by default, because a loose match out-ranks the exact one.
await parser.actions.search("varibles", { fuzzy: true }) → SET_VARIABLES…
search(query, { includeDeprecated: true })Retired members and definitions too. Off by default, like list(): searching is how a caller finds what to use.
await parser.actions.search("sandbox") → the live ones only

Scopes with a root

pipelines, targets, environments, sandboxes, agents, domains, customActions, distributions

root() gives the document's entry type: what a YAML file at that path may contain. await parser.pipelines.root() returns the PipelineYaml document.

Scopes with a union

actions, targets, environments

MethodWhat it does
list(options?)The discriminator values the union accepts, minus the ones the schema retired.
await parser.targets.list() → ["SSH", "FTP", "MATCH", …]
{ includeDeprecated: true } lists those too. get() and search() answer for a retired member either way.
get(value)One member in full: name, docs slug, tags, required fields, examples, properties, and the shaped document.
await parser.actions.get("BUILD")
outputs holds what an action leaves behind: files with a path and description, and variables with a name, description and example. Both lists are empty for a member that declares none.
(await parser.actions.get("HTTP")).outputs.variables → BUDDY_HTTP_ACTION_RESPONSE_STATUS…
commonFields()Fields every member of the union declares.
await parser.actions.commonFields() → ["type", "action", "trigger_time", …]

pipelines

MethodWhat it does
eventVariables(type?)The variables a run exposes, by the event that started it. Each has a name, description and example. Without a type, every event type that exposes any.
await parser.pipelines.eventVariables("PUSH") → BUDDY_RUN_EVENT_TYPE…
An event type that exposes none, such as SCHEDULE, gives an empty list.

api

MethodWhat it does
operations()Every operation the document declares, with its summary, operationId, tags, deprecated and security. Nothing is resolved, so listing every endpoint costs one walk rather than one per operation.
await parser.api.operations()
endpoint(method, path, variant?)The operation as a document: parameters, request body, responses. Pass variant to pick one branch of a union body.
await parser.api.endpoint("POST", "/oauth2/register")
Bodies are a list: request.contents and each response's contents hold one entry per media type, in the document's order, so a download offering a byte stream and a JSON envelope reports both. A status that declares no content has none.
variants(method, path)The discriminator values you can pass as variant. Empty when the body branches by named type instead. Those shapes are on the document, under request.contents[0].body.variants.
type(name)One named schema as a document, for a type name you read off a field. Saves refetching the endpoint it came from.
await parser.api.type("ExecutionView")
typeNames(options?)Every schema name the document declares, minus the retired ones.
search(query, options?)As above, over operation summaries, paths and tags.

On the client

MethodWhat it does
search(query, options?)One index across every scope, YAML and REST together. Narrow it with { scopes: ["actions"] }. Twenty hits unless limit says otherwise.
Each hit carries kind, scope, key, name, tags, score.
raw(document)The unresolved document, $ref pointers intact. For a validator compiling against them, or for reading $defs directly.
prefetch(documents?)Load now rather than on the first request. Under from() this reports { refreshed: [], reason: "documents were supplied" } rather than throwing.
release(document)Drop a fetched document and its resolution memo. The shaped-result cache is shared, so it is emptied whole rather than per document. Returns false for a supplied document, which cannot be reacquired.
stats()Which documents are held, their ETag and fetch time, which deployment they came from, whether a write to the cache failed, and the result cache's entries, hits and misses.

Options

await BuddySchema.load({ environment, cacheDir, ttlMs, results });
BuddySchema.from(documents, { results });

environment is one of the names above. cacheDir and ttlMs fall back to the variables below. results bounds how many shaped documents are held. Pass false to shape every call afresh.

Errors

  • DocumentUnavailableError: nothing fetched and no cached copy. Names the URL and points at BuddySchema.from().
  • MissingDocumentError: a scope was reached whose document from() was not given. Lists what was.
  • ScopeUnsupportedError: a supplied document is not the schema that scope needs. Says what was expected, what was found, and the nearest existing name.

Validating a YAML file

@buddy-works/docs-schema/validate checks a YAML file against the schema of a scope with a root, the same schema the methods above read. Any other scope throws.

import { validateYaml } from "@buddy-works/docs-schema/validate";

const report = await validateYaml(parser, "pipelines", yaml);

The report holds findings, checked and blocked. blocked is true only when the file cannot be read as that scope's document at all. Every other finding is advice, and what to refuse on is the caller's call.

Each finding has a severity, a message and a path such as pipelines[0].actions[1].type, which together read as one line. For anything else, use the rest:

  • at: the same location as segments, [0, "actions", 1, "type"]. A finding about a key points at the key.

  • code: what went wrong, with the details beside it:

    codeDetailsMeaning
    syntaxline?, column?The YAML does not parse, or an alias in it has no anchor or expands without end. Blocks. Aliases carry no position.
    documentexpected, gotNot the kind of file the scope takes, a list of entries or one entry. An empty file is neither. Blocks.
    emptyA list with no entries.
    shapeexpected, gotA value of the wrong kind, such as a word where an object belongs.
    missingTypekeyAn item that does not name its type.
    unknownTypekey, value, suggestion?A type the union does not have, with the nearest one it has when one is close.
    missingKeykeyA required key that is absent.
    emptyValuekeyA required key written with no value. A warning: Buddy saves it as an empty string.
    emptyItemA list item written with no value. Buddy refuses the file.
    unknownKeykey, suggestion?A key the schema does not declare. An error where the schema closes the object, a warning where it leaves it open, nothing where it checks such keys by a rule of its own (a value that breaks the rule is a schema finding).
    schemakeyword, paramsAny other rule of the schema.
  • declaredIn: on an unknown key, where the keys allowed there are declared, either an action type or a definition in a scope. Point a reader at it, such as with parser.actions.get(type) or parser.pipelines.def(name).

checked lists each entry, list item and object section that could be matched to a definition and checked against it. An item missing its type, of a type the union lacks, or of the wrong kind is not on it. Each one comes with that definition (a plain value has none) and the variant a union chose. A node passed when no finding's at starts with its own.

After Buddy saves a file, it may have dropped keys or renamed them. reconcileWithSaved(findings, sentYaml, savedYaml) compares the file that was sent with the file that was saved:

  • It drops a finding about a key the saved file has, under that name or under another.
  • It marks an unknown key that the saved file lost as not saved by Buddy.

Requirements

Node 20.19 or newer, or Bun.

The cache lives in ~/.buddy/docs-schema, one pair of files per document: the body and its ETag. Two environment variables override the defaults:

  • BUDDY_DOCS_SCHEMA_CACHE: cache directory.
  • BUDDY_DOCS_SCHEMA_TTL: milliseconds a cached copy stays fresh. 0 revalidates on every access. Infinity pins a run to the version it started with.

版本与发布记录

@buddy-works/docs-schema 共发布 5 个版本,首次发布于 2026/09/02;过去 12 个月发布了 5 个版本。

版本发布日期距今
0.1.4latest2026/11/053 天前
0.1.32026/09/101 个月前
0.1.22026/09/072 个月前
0.1.12026/09/042 个月前
0.1.02026/09/022 个月前

关于 @buddy-works/docs-schema 的常见问题

@buddy-works/docs-schema 是什么?

Buddy's YAML and REST API schemas, fetched, cached and resolved into documented shapes

如何安装 @buddy-works/docs-schema?

在项目目录运行 npm install @buddy-works/docs-schema,或使用 pnpm add / yarn add / bun add。

@buddy-works/docs-schema 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@buddy-works/docs-schema 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@buddy-works/docs-schema 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js ^20.19.0 || >=22.12.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@buddy-works/docs-schema 可以商用吗?

@buddy-works/docs-schema 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@buddy-works/docs-schema 的下载量有多少?

根据 npm 官方下载统计,@buddy-works/docs-schema 在 2026/10/09 – 2026/11/07 这 30 天内被下载 2,804 次,日均约 93 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@buddy-works/docs-schema 还在维护吗?

最新版本 0.1.4 发布于 2026/11/05(3 天前);至今共发布 5 个版本,其中过去 12 个月发布了 5 个;首次发布于 2026/09/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。