openapi

package
v0.0.0-...-8d9931c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 29, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package openapi lowers OpenAPI 3.0/3.1/3.2 documents into the Morphic IR. It implements compilers.Compiler.

What is here is the compiler's public face and the assembly behind it: the Compiler, its Options and the vocabulary they answer to as text, the recognition of an OpenAPI document from its own bytes, the document metadata, and the run that calls the lowerings in order and builds a Document out of what they return.

Parsing is delegated to github.com/speakeasy-api/openapi. Everything the compiler itself decides — identity (pointer-derived IDs), hoisting, normalization (nullable spellings, allOf classification), and the lossless preservation of constructs the IR does not model structurally — lives in the packages under internal/, each of which states its own place in the order.

Index

Constants

View Source
const (
	// GroupByTags groups operations by their first OpenAPI tag (default).
	GroupByTags = lowering.GroupByTags
	// GroupByPathPrefix groups operations by the first path segment.
	GroupByPathPrefix = lowering.GroupByPathPrefix
)

Grouping strategies.

View Source
const (
	// TargetDeprecationMessage fills ir.Deprecation.Message.
	TargetDeprecationMessage = lowering.TargetDeprecationMessage
	// TargetDeprecationSince fills ir.Deprecation.Since.
	TargetDeprecationSince = lowering.TargetDeprecationSince
	// TargetDeprecationRemovalVersion fills ir.Deprecation.RemovalVersion.
	TargetDeprecationRemovalVersion = lowering.TargetDeprecationRemovalVersion
	// TargetDeprecationRemovalDate fills ir.Deprecation.RemovalDate.
	TargetDeprecationRemovalDate = lowering.TargetDeprecationRemovalDate
	// TargetEnumOpen clears ir.Enum.Closed.
	TargetEnumOpen = lowering.TargetEnumOpen
)

The typed fields promotion can fill today.

View Source
const (
	// DefaultMaxSourceBytes is the byte budget: 64 MiB, a 5.2x margin over the
	// largest measured description. It is the only budget enforceable before the
	// source is parsed, which is why it is the loosest — it exists to bound the
	// parse itself, and everything finer is measured after it.
	DefaultMaxSourceBytes = 1 << 26
	// DefaultMaxSourceNodes is the parsed-node budget: 2,097,152, a 4.4x margin
	// over the largest measured description. Node count, not byte count, is what
	// the phases after the parse cost — building the typed model and resolving
	// its references peaked at 1.5 GB of RSS for GitHub's 471,735 nodes — so this
	// is the budget that bounds a compile, and the byte budget above is only what
	// gets a document far enough to be counted.
	DefaultMaxSourceNodes = 1 << 21
	// DefaultMaxEnumMembers is the per-enum member budget: 65,536, a 109x margin
	// over the largest measured enum and roughly 7x the largest registry an API
	// might reasonably inline (IATA airport codes, BCP-47 language subtags, both
	// under 10,000 entries).
	//
	// An enum is the one construct whose IR cost per source node is
	// disproportionate: every member becomes an ir.EnumMember with a canonical
	// word sequence of its own, and a heterogeneous one becomes a hoisted Literal
	// type plus a union variant per member. That is the amplification GitHub #75
	// reports: one 1,000,000-member enum turning 10 MB of source into 2.6 GB of
	// peak RSS, on a document well inside the node budget above — which is why
	// that budget does not cover this one. An enum at this budget compiles in
	// under 200 MB.
	DefaultMaxEnumMembers = 1 << 16
	// DefaultMaxAliasSurplus is the alias budget: 262,144 nodes that YAML aliases
	// may add to a document beyond its own. It is measured against a different
	// corpus from the budgets above, because those three descriptions barely
	// alias: 1,693 real OpenAPI and Swagger specs (1,491 from APIs.guru, 199
	// hand-authored ones chosen for their anchors, and the three above), whose
	// largest surplus is 15,727 nodes — a 16.7x margin.
	//
	// It is the bound that sets how much memory an alias-heavy document can cost,
	// since the parser builds a fresh subtree for every path through an alias.
	// Padding a document's own size lifts every relative bound out of the way,
	// and this one is not relative: at 1<<20 a purpose-built 93 KiB document was
	// measured peaking at 4.4 GiB; at this default the largest still accepted
	// peaks at 1.65 GiB.
	DefaultMaxAliasSurplus = 1 << 18
)

Compiled-in defaults for Limits, each calibrated against measured documents rather than chosen round. The measurements are of three flagship public descriptions, taken 2026-08-09: GitHub's REST API (12,920,264 bytes, 471,735 parsed YAML nodes, largest enum 53 members), Stripe's (7,967,776 bytes, 265,846 nodes, largest enum 599 members) and Kubernetes' aggregated Swagger (4,475,339 bytes, 138,990 nodes, no enum at all).

Variables

This section is empty.

Functions

func DefaultExtensionPromotions

func DefaultExtensionPromotions() map[string]ExtensionTarget

DefaultExtensionPromotions returns the extension-to-field mapping applied when the caller names none. It is exported so a caller changing the mapping can start from it rather than transcribe it.

func DefaultStreamingMediaTypes

func DefaultStreamingMediaTypes() []string

DefaultStreamingMediaTypes returns the media types StreamingMedia classifies as streams when the caller names none. It is exported so a caller extending the list can start from it rather than transcribe it.

Types

type Compiler

type Compiler struct{}

Compiler lowers OpenAPI 3.x documents into the IR.

func New

func New() *Compiler

New returns the OpenAPI compiler.

func (*Compiler) Compile

func (c *Compiler) Compile(ctx context.Context, sources []compilers.Source, opts compilers.Options) (*ir.Document, []ir.Diagnostic, error)

Compile implements compilers.Compiler. Milestone 1 accepts exactly one root source; multi-document stitching belongs to the link pass.

func (*Compiler) DecodeOptions

func (*Compiler) DecodeOptions(set compilers.OptionSet) (any, error)

DecodeOptions implements compilers.Compiler: it turns textual settings into an Options value. Settings are read in sorted order so that a set with two bad values always reports the same one.

func (*Compiler) Detect

Detect implements compilers.Compiler. It reports the dialect src declares, keyed by the major.minor prefix of the version string.

It names swagger@2.0 as well, which this compiler does not serve: a Swagger document is recognizably an API spec, and reporting it as one lets the caller say the format is unsupported rather than that the file is unreadable. The path is not consulted — an OpenAPI document is what it declares itself to be, under any extension.

Bytes the probe cannot read are declined silently unless they declare a version under one of the discriminating keys, in which case the reader's complaint is reported: a source that says `openapi: 3.1.0` and will not read is this compiler's own and broken, which nothing else is in a position to say. Bytes that declare no version are another format's, and a YAML parser's complaint about them describes only the parser that was wrong to be asked. That holds whether they parse or not: a key with prose beside it is what Markdown writes at column 0 (declaredVersions for a document that parses, declaresProbeKey for one that does not), and a mapping under it is another tool's configuration section. What tells either from a declaration is the one word after the colon.

The parse a recognition carries is the one Compile lowers. Recognizing a source means reading what it declares, which means parsing it, and the compile that follows would otherwise parse the same bytes again. Every source within the byte budget is parsed whole, one this compiler goes on to refuse or another format's alike: the budget is what bounds that cost, not a second reader that answers for large sources by reading less of them.

A source past the byte budget in opts is declined before any of it is read, with the refusal Compile would give it. The budget exists to bound reading the source, and detection is a read of it, so the one a caller set holds here as it does in the compile. It is reported rather than silent because it is the reason nothing took the source, whatever its format: saying no compiler recognized it would send the caller to look at the document rather than at the budget. A registry reads it only when no compiler takes the source, so it costs another format's compiler nothing.

func (*Compiler) Formats

func (*Compiler) Formats() []compilers.SourceFormat

Formats reports the OpenAPI dialects this compiler accepts.

type ExtensionPromotions

type ExtensionPromotions = lowering.ExtensionPromotions

ExtensionPromotions is the vendor-extension promotion policy: which x-* keys are read into which typed IR field. It is another injectable-policy seam (architecture principle 6), and is named here rather than restated for the reason GroupingStrategy is.

type ExtensionTarget

type ExtensionTarget = lowering.ExtensionTarget

ExtensionTarget names one typed IR field a promoted extension fills.

type GroupingStrategy

type GroupingStrategy = lowering.GroupingStrategy

GroupingStrategy selects how operations are grouped into OperationGroups. It is the injectable-policy seam (architecture principle 6): grouping is inferred policy, not source semantics, and can be switched or disabled.

The vocabulary is declared once, beneath both walks, and named here rather than restated: two declarations of one strategy set can drift, and a strategy only the public half knew would fall through to the default unreported.

type Limits

type Limits struct {
	// MaxSourceBytes bounds one source document's size in bytes, checked before
	// it is parsed.
	MaxSourceBytes int `json:"maxSourceBytes,omitzero"`
	// MaxSourceNodes bounds the YAML nodes one source document parses to,
	// checked before the typed model is built from it.
	MaxSourceNodes int `json:"maxSourceNodes,omitzero"`
	// MaxEnumMembers bounds the members of a single enum. An enum past it lowers
	// as the top type with an error diagnostic naming the budget; the rest of the
	// document still lowers.
	MaxEnumMembers int `json:"maxEnumMembers,omitzero"`
	// MaxAliasSurplus bounds the nodes YAML aliases may add to one source
	// document, or to its overlay, beyond the document's own: what the document
	// costs once every alias stands in for a copy of what it names, less what it
	// costs as written. A document with no alias adds nothing, so this never
	// refuses one for its size. A source past it is refused before the typed
	// model is built from it, where that cost would be paid, and an overlay
	// before it is applied.
	//
	// Turning it off does not turn off alias refusal. A document whose aliases
	// expand it past both 128 times its own size and 32,768 nodes is refused
	// whatever this says, as openapi/alias-amplification rather than
	// openapi/budget-exceeded, because that is the shape of a bomb rather than of
	// a large document. What is left unbounded is how far a document within that
	// ratio may expand, so its cost is at most that multiple of its own size.
	MaxAliasSurplus int `json:"maxAliasSurplus,omitzero"`
}

Limits bounds the size and cardinality of one compile, so that an input which is legal but pathologically large is refused with a diagnostic rather than left to exhaust the host (GitHub #75).

It is policy rather than semantics (architecture principle 6): what counts as pathological depends on the machine doing the compiling, so every budget here is the caller's to set. In each field zero takes the documented default and a negative value means unbounded — that budget refuses nothing, which is the escape hatch for a caller who has measured their own input and their own machine. A field says so where a constant beside its budget still applies.

These are budgets on the size of the input. They are not the only bounds the compiler enforces: schema nesting depth, how many times its own size a document's aliases expand it to, reference-chain length and several walk node counts are each bounded by a constant beside the code that walks them, because none of those describes something a caller could legitimately want more of.

type Options

type Options struct {
	// Grouping selects the operation-grouping strategy.
	Grouping GroupingStrategy `json:"grouping,omitempty"`
	// StreamingMedia selects which media types imply a stream. The zero value is
	// the default list, on; a caller who wants only what a document declares
	// disables it.
	StreamingMedia StreamingMedia `json:"streamingMedia"`

	// Promotions selects which vendor extensions are read into typed IR fields.
	// The zero value is the default mapping, on; a caller who wants extensions
	// kept verbatim and nothing more disables it.
	Promotions ExtensionPromotions `json:"promotions"`
	// AllowExternalRefs lets reference resolution leave the source document —
	// reading files off disk and fetching http(s) URLs. Off by default, because
	// compilers.Source is the whole input ("the caller loads bytes so compilation
	// stays pure and reentrant") and a spec is untrusted data whose $refs would
	// otherwise name any readable file or reachable host. A $ref leaving the
	// document is reported unresolved instead of followed.
	//
	// Turning it on departs from that contract knowingly, and buys less than it
	// looks: resolution reads relative to the process working directory, so the
	// same bytes compile differently in two directories, and the resolved content
	// still does not reach lowering — Sources records one entry either way
	// (GitHub #74 carries the multi-file work).
	AllowExternalRefs bool `json:"allowExternalRefs"`
	// Overlay is an OpenAPI Overlay document to apply to the source before
	// lowering, or nil for none. It is the source-document patching hook
	// architecture §2.2 names, and is deliberately not the IR overlay pass beside
	// it: a fix that has to land before naming and hoisting heuristics read the
	// broken shape cannot be made afterwards.
	Overlay *Overlay `json:"overlay,omitzero"`
	// Limits bounds how large an input this compile will lower. The zero value
	// takes every default.
	Limits Limits `json:"limits"`
}

Options configures the OpenAPI compiler. It is the concrete type this compiler expects in compilers.Options.FormatOptions; the zero value is valid and normalized by withDefaults.

It stays whole here while each phase below takes its own input projected from it — load.Options, and the strategy the lowering context carries. Its shape is a published contract, since a caller outside this package constructs this exact type by field, and most of it describes work no single phase does.

type Overlay

type Overlay struct {
	// Path names the overlay document. It is recorded as the overlay's
	// SourceInfo path and never opened.
	Path string `json:"path,omitempty"`
	// Data is the overlay document's bytes.
	Data []byte `json:"data,omitempty"`
	// Lax turns off strict application.
	//
	// Strict — the zero value — is the default because an action whose selector
	// matches nothing is nearly always a typo in a JSONPath, and an overlay that
	// silently does nothing ships an SDK missing the very fix it was written to
	// make. Under strict such an action is reported and the compile refuses;
	// under lax it is not reported at all.
	Lax bool `json:"lax,omitzero"`
}

Overlay is one pre-read OpenAPI Overlay document (the Overlay Specification's own format, applied with JSONPath selectors) and how strictly to apply it.

The document arrives as bytes, like the spec itself, because a compiler performs no file I/O — reading it is the caller's job, which is what keeps compilation pure and reentrant. A programmatic caller sets this through engine.RunOptions.FormatOptions, which the engine forwards verbatim; a caller who has only text names the file with the "overlay" setting and the reader in compilers.OptionSet loads it, so the read is still the caller's.

An applied overlay becomes a second entry in Document.Sources, and every position it introduced or rewrote names that entry as its Provenance.Source. The positions it left alone keep the source's own line and column, because the overlay is applied to the parsed node tree rather than to re-serialised bytes.

type StreamingMedia

type StreamingMedia = lowering.StreamingMedia

StreamingMedia is the media-type streaming policy: which media types imply that a body is a sequence of frames when the document declares nothing that says so. It is another injectable-policy seam (architecture principle 6), and it is named here rather than restated for the reason GroupingStrategy is.

Directories

Path Synopsis
internal
annotation
Package annotation reads the documentation-adjacent facts a schema or a carrier declares — descriptions, deprecation, visibility, XML hints, extensions — and the validation-only JSON Schema keywords the IR keeps verbatim rather than models.
Package annotation reads the documentation-adjacent facts a schema or a carrier declares — descriptions, deprecation, visibility, XML hints, extensions — and the validation-only JSON Schema keywords the IR keeps verbatim rather than models.
auth
Package auth lowers what a document says about authentication: the security schemes it declares, and the requirements that name them.
Package auth lowers what a document says about authentication: the security schemes it declares, and the requirements that name them.
diag
Package diag holds the OpenAPI compiler's diagnostic vocabulary: the stable codes it reports under, and the single constructor that builds a diagnostic from them.
Package diag holds the OpenAPI compiler's diagnostic vocabulary: the stable codes it reports under, and the single constructor that builds a diagnostic from them.
ids
Package ids builds the RFC 6901 pointer that names a position — a jsontext.Pointer from construction on, read back through its methods, never split on '/' — and derives IR identifiers and namespaces from it.
Package ids builds the RFC 6901 pointer that names a position — a jsontext.Pointer from construction on, read back through its methods, never split on '/' — and derives IR identifiers and namespaces from it.
load
Package load turns one source document into a parsed, reference-resolved OpenAPI document plus the identity metadata the rest of the compiler stamps into the IR.
Package load turns one source document into a parsed, reference-resolved OpenAPI document plus the identity metadata the rest of the compiler stamps into the IR.
lowering
Package lowering holds the immutable context every OpenAPI lowering reads.
Package lowering holds the immutable context every OpenAPI lowering reads.
merge
Package merge reconciles the properties more than one allOf branch declares: either folding two declarations into one or reporting that they disagree.
Package merge reconciles the properties more than one allOf branch declares: either folding two declarations into one or reporting that they disagree.
nodeview
Package nodeview reads a YAML mapping the way the resolver will: through aliases, through `<<` merge keys, and with duplicate keys resolved the way the parser resolves them.
Package nodeview reads a YAML mapping the way the resolver will: through aliases, through `<<` merge keys, and with duplicate keys resolved the way the parser resolves them.
openapitest
Package openapitest holds the test scaffolding every test package under compilers/openapi would otherwise carry as its own copy.
Package openapitest holds the test scaffolding every test package under compilers/openapi would otherwise carry as its own copy.
operation
Package operation lowers what a document says an API does: its path items, webhooks and callbacks, the parameters merged onto each operation, and the content of every request body, response and header.
Package operation lowers what a document says an API does: its path items, webhooks and callbacks, the parameters merged onto each operation, and the content of every request body, response and header.
overlay
Package overlay applies an OpenAPI Overlay document to the parsed node tree, and records which positions in the result the overlay is answerable for.
Package overlay applies an OpenAPI Overlay document to the parsed node tree, and records which positions in the result the overlay is answerable for.
resolve
Package resolve answers what a $ref names: which same-document pointer it addresses, which interned type, if any, already lives there, and — for the components that are not schemas — which concrete value and declaration site a reference-or-inline entry stands for.
Package resolve answers what a $ref names: which same-document pointer it addresses, which interned type, if any, already lives there, and — for the components that are not schemas — which concrete value and declaration site a reference-or-inline entry stands for.
scan
Package scan refuses a source document before any of it is lowered.
Package scan refuses a source document before any of it is lowered.
schema
Package schema lowers OpenAPI schemas into IR types: the shape walk itself, the compositions written around it, the references that reach other schemas, and the preservation of what the IR has no field for.
Package schema lowers OpenAPI schemas into IR types: the shape walk itself, the compositions written around it, the references that reach other schemas, and the preservation of what the IR has no field for.
sourceindex
Package sourceindex answers, in one walk, the questions asked of a decoded source tree before any of it is lowered.
Package sourceindex answers, in one walk, the questions asked of a decoded source tree before any of it is lowered.
value
Package value lowers source scalars into ir.Value, keeping numeric literals as their exact source text so nothing rounds through float64.
Package value lowers source scalars into ir.Value, keeping numeric literals as their exact source text so nothing rounds through float64.
ynode
Package ynode spells yaml.v3 nodes: the tag a resolved `<<` merge key carries, the constructors for the node kinds a parse produces, and the merge chain the compiler's depth bounds are measured against.
Package ynode spells yaml.v3 nodes: the tag a resolved `<<` merge key carries, the constructors for the node kinds a parse produces, and the merge chain the compiler's depth bounds are measured against.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL