resolve

package
v0.0.0-...-3d40d98 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

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.

It is deliberately only that. Following a reference far enough to lower what it points at is schema lowering reached through a reference, not resolution, and it recurses back into the schema walk — so it stays with the walk rather than crossing this boundary. What is here needs nothing but the document's own path, what it declares, and a registry to look an ID up in, which is why it is a package at all.

Reference resolution is not promoted to compilers/compile: two of the three compilers need it and need it by different mechanisms, which is the shape that looks promotable and is not.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InternedID

func InternedID(ts *compile.Types, pointer string) (ir.TypeID, bool)

InternedID returns the TypeID a node was interned under at pointer, when one already exists there — either a previously hoisted sub-schema (via byPointer) or a node registered directly under its pointer-derived ID.

func IsRefSite

func IsRefSite(js *oas3.JSONSchema[oas3.Referenceable], s *oas3.Schema) bool

IsRefSite reports whether a position is $ref-shaped: the resolver's own IsReference (a non-empty $ref), or a schema body that carries a Ref field of its own even when empty. That is deliberately broader than annotation.At's classification: schemaRef, refTargetSchema, and bodySchemaPointer (content.go) all need "there is a $ref-carrying body here," not "there is a genuine, followable reference," so the degenerate {$ref: ""} shape counts for them even though it does not count as an annotation.Reference. s is the schema body the caller already holds; a nil s (a boolean schema carries none) is never $ref-shaped.

func Object

func Object[T, S any, R interface {
	*S
	Referenced[T, S]
}](ref R) *T

Object returns the concrete value of a reference-or-inline entry, preferring the inline object and falling back to the resolved target. The *S term constrains R to a pointer type so `ref == nil` is legal in generic code; interfaces.Validator[T] is unavailable here (it lives in the library's internal/ tree), which is why R is expressed via *S rather than V directly.

func ObjectAt

func ObjectAt[T, S any, R interface {
	*S
	Referenced[T, S]
}](scope Scope, ref R, usePtr string) (*T, string)

ObjectAt returns a reference-or-inline entry's concrete value together with the pointer of the declaration it resolves to, walking through any chained component aliases to the last one written in this document (issue #107). usePtr — the entry's own position — stands whenever the chain has no declaration addressable here: an inline entry, a reference that leaves this document, or one that outruns maxRefChain. An alias pointer is never returned on its own, since a one-key $ref object has no children to hoist.

func TargetSchema

func TargetSchema(js *oas3.JSONSchema[oas3.Referenceable], ref *oas3.Schema) *oas3.Schema

TargetSchema returns the resolved target schema when js is a $ref, so use-site annotations can fall back to the referent; it returns nil otherwise.

Types

type Referenced

type Referenced[T, S any] interface {
	GetObject() *T
	GetResolvedObject() *T
	GetReference() references.Reference
	GetReferenceResolutionInfo() *references.ResolveResult[S]
}

Referenced is the method set every soa "Referenced*" alias exposes: it is the generic form of speakeasy's Reference[T, V, C], which underlies ReferencedPathItem, ReferencedResponse, ReferencedHeader, ReferencedCallback, ReferencedParameter, ReferencedRequestBody, ReferencedExample, and ReferencedSecurityScheme alike. Naming the shape once here lets Object stand in for what would otherwise be one resolver per aliased type. S is the reference's own type, Reference[T, V, C]; ObjectAt walks it through GetReferenceResolutionInfo to follow a chain of component aliases.

type Scope

type Scope struct {
	// SelfPath is the source path of the document being compiled, against which
	// a reference's document part is judged internal or external.
	SelfPath string
	// Declares reports whether the document declares a component schema of this
	// name.
	Declares func(name string) bool
}

Scope is what resolving a reference needs to know about the document doing the referencing: which file it is, and which component schemas it declares.

Declares is a predicate rather than the name set itself, for the reason the lowering context keeps that set behind an accessor: a copied struct shares a map, and a reader has no business writing to one.

func (Scope) ComponentRef

func (s Scope) ComponentRef(pointer string) (id ir.TypeID, ok, handled bool)

ComponentRef resolves an internal pointer addressing a top-level component schema to its stable named ID, but only when that component is declared. It returns handled=true once the pointer is classified as a component pointer (declared or not), so callers can stop; a declared component yields ok=true, an undeclared one ok=false (a dangling reference to drop). The ID is rebuilt from the component's canonical name — unescaped, then re-escaped by ids.Ptr — rather than from the incoming pointer text, so a non-canonically escaped reference (e.g. `A~B` for a component named "A~B", interned under `A~0B`) still resolves to the interned node instead of an unbacked ID.

func (Scope) InternalPointer

func (s Scope) InternalPointer(ref string) (string, bool)

InternalPointer returns the same-document JSON pointer a $ref (or discriminator mapping) target addresses, and ok=false for a genuine cross-document reference, a bare schema name, or a malformed ref. A document part naming this same source file (an OpenAPI self-reference) is treated as internal — Milestone 1 interns only same-file targets; genuinely external ones are diagnosed and dropped.

The split into document and pointer is the resolver's own (references.Reference, v1.24.0) rather than a hand-rolled one: a $ref is a URI, so its fragment is percent-encoded, and `#/components/schemas/Foo%2DBar` names the component "Foo-Bar". Comparing the raw fragment against declared names instead reported a reference the resolver had resolved as unresolved, degrading the position to `any` and dropping any discriminator mapping that spelled its target that way. The pointer returned here is also an ID source, so the quieter half cost more: an encoded pointer interned a second node for a position an unencoded pointer already named, leaving one coordinate with two types and no diagnostic either side of it (GitHub #40). Asking the resolver is what stops the answer drifting from it again; nodeview.InternalPointer mirrors the same two methods for the cycle scan, and records what a dependency bump should re-check.

A fragment that is not a JSON pointer is refused here rather than passed on. `#addr` names a JSON Schema `$anchor`, not a coordinate, and Milestone 1 resolves no anchors; letting it through returned "addr" as though it were a pointer, and every ID derived from it was a path no source coordinate spells (GitHub #141). The resolver library happens to reject it too, but relying on that puts the refusal outside this compiler, where a library that started resolving anchors would silently reinstate the malformed derivation.

func (Scope) NamesReferent

func (s Scope) NamesReferent(js *oas3.JSONSchema[oas3.Referenceable], ref string) bool

NamesReferent reports whether ref names a schema this compilation can point a TypeRef at, answering the question resolveSchemaRef answers without interning anything on the way. A classifier needs that: deciding how to lower a schema must not hoist nodes as a side effect of asking.

It sits beside resolveSchemaRef because it must stay in step with it, and mirrors it minus the two steps that are not pure lookups — hoistSubSchema, which interns (its own only failure is a target that declares no schema body, which is the last condition here), and the internedID cache hit, which would make the answer depend on which schema happened to lower first.

Jump to

Keyboard shortcuts

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