openpkg-tsryanwaits/openpkg-ts
A machine-readable standard for package APIs
- Problem
- TypeDoc, API Extractor, TSDoc: every tool emits its own private format. No standard, no interop.
- Standard
- OpenPkg is the spec, hosted at openpkg.dev; openpkg-ts is its TypeScript reference implementation.
- Extract
extractSpec()gives you the full public API as a typed spec.- Output
- Functions, types, interfaces, generics. Resolved JSON out.
- Agents
- Primitives first: extractSpec, diffSpec, recommendSemverBump. The CLI is a thin wrapper over the same SDK.
No Standard, No Interop
REST APIs have OpenAPI. GraphQL has introspection. TypeScript has tools: TypeDoc emits JSON, API Extractor emits .api.json, TSDoc standardizes comments. None of it interoperates. Each format is one tool's working memory, versioned to that tool, readable mostly by that tool.
So every consumer that needs a package's API surface, doc generators, diff tools, CI gates, agents, re-extracts it from source with the compiler and keeps the result to itself. Same function, two incompatible answers:
{"name": "__namedParameters","type": { "type": "reference","target": 14 }}// internal reflection model:// names no caller typed,// ids into TypeDoc's own tree
{"kind": "Function","excerptTokens": [{ "kind": "Content","text": "declare function " }]}// token stream built for// Microsoft's docs pipeline
One Function Call
OpenPkg is the interchange format those tools never agreed on: a versioned spec, hosted at openpkg.dev, with JSON Schema doing the type representation. openpkg-ts extracts it from any package in one call: functions, types, interfaces, classes, generics.
import { extractSpec } from '@openpkg-ts/sdk';const { spec, diagnostics } = await extractSpec({entryFile: './src/index.ts',});
What the Spec Holds
An OpenPkg document describes every public export as a contract another tool can consume. Named types resolve as real references. A literal union becomes a real enum. A destructured parameter keeps its names and defaults, because that's the function's actual calling contract.
{"openpkg": "0.4.0","exports": [{"name": "connect","kind": "function","parameters": [{"name": "options","schema": {"type": "object","properties": {"host": { "type": "string" },"port": { "type": "number", "default": 5432 },"mode": { "enum": ["json", "markdown", "html"] }}}}],"returns": { "$ref": "#/types/Client" }}]}
Primitives First
One document, many consumers: diffSpec, recommendSemverBump, and validateSpec all take the same spec that extractSpec produced. That's the interop the private formats never had. An agent imports the function and gets a typed result back; the CLI is a thin wrapper over the same SDK.
import { diffSpec, recommendSemverBump } from '@openpkg-ts/spec';const diff = diffSpec(oldSpec, newSpec);// { added, removed, breaking, docsOnly }const { bump, reason } = recommendSemverBump(diff);// bump: 'major' | 'minor' | 'patch'
Three Packages
sdk for extraction, spec for validation and diffing, cli for humans (a thin wrapper over the sdk). Point extractSpec at any entry file and get a typed spec back, no config.
npm install @openpkg-ts/sdk @openpkg-ts/spec