Core utilities for working with OpenAPI documents including reference resolution, extension property handling, and type guards. These utilities work with both OpenAPI 2.0 and 3.0 documents.
Utilities for resolving $ref references in OpenAPI documents with circular dependency detection.
/**
* Gets an object instance for the item, regardless if it's a reference or not.
* @param document - Entire OpenAPI document
* @param item - Reference item or direct object
* @param stack - Stack for circular dependency detection
* @returns Dereferenced object with metadata
*/
function dereference<T extends {} | undefined>(
document: any,
item: Refable<T>,
stack?: string[]
): Dereferenced<T>;
/**
* Identifies if a given refable is a reference or an instance
* @param item - Check if item is a reference
* @returns Type guard indicating if item is PathReference
*/
function isReference<T extends {} | undefined>(item: Refable<T>): item is PathReference;Usage Examples:
import { dereference, isReference, OpenAPI3Document } from "@azure-tools/openapi";
const document: OpenAPI3Document = {
openapi: "3.0.0",
info: { title: "Test", version: "1.0" },
paths: {},
components: {
schemas: {
User: {
type: "object",
properties: {
id: { type: "integer" },
profile: { $ref: "#/components/schemas/Profile" }
}
},
Profile: {
type: "object",
properties: {
name: { type: "string" }
}
}
}
}
};
// Check if something is a reference
const profileRef = document.components?.schemas?.User?.properties?.profile;
if (profileRef && isReference(profileRef)) {
console.log("It's a reference:", profileRef.$ref);
// Resolve the reference
const resolved = dereference(document, profileRef);
console.log("Resolved to:", resolved.instance);
console.log("Reference name:", resolved.name); // "Profile"
console.log("From reference:", resolved.fromRef); // true
}Utilities for working with OpenAPI extension properties (keys starting with "x-").
/**
* Returns true if the key starts with `x-`
* @param key - Key to check
* @returns Type guard for extension keys
*/
function isExtensionKey(key: string | ExtensionKey): key is ExtensionKey;
/**
* Only return properties starting with x-
* @param dictionary - Object to filter
* @returns Array of extension keys
*/
function includeXDashKeys<T extends Record<string | ExtensionKey, any>>(
dictionary: T,
): Extract<keyof T, ExtensionKey>[];
/**
* Only return properties NOT starting with x-
* @param dictionary - Object to filter
* @returns Array of non-extension keys
*/
function omitXDashKeys<T extends {}>(dictionary: T): Exclude<keyof T, ExtensionKey>[];
/**
* Extract only extension properties from an object
* @param obj - Object to filter
* @returns Object with only x-* properties
*/
function includeXDashProperties<T extends Record<string | ExtensionKey, any> | undefined>(
obj: T,
): T extends undefined ? undefined : Pick<T, Extract<keyof T, ExtensionKey>>;
/**
* Remove extension properties from an object
* @param obj - Object to filter
* @returns Object without x-* properties
*/
function omitXDashProperties<T extends {} | undefined>(
obj: T,
): T extends undefined ? undefined : Pick<T, Exclude<keyof T, ExtensionKey>>;Usage Examples:
import {
isExtensionKey,
includeXDashProperties,
omitXDashProperties
} from "@azure-tools/openapi";
const schema = {
type: "object",
properties: { name: { type: "string" } },
"x-custom-property": "custom-value",
"x-vendor-extension": { some: "data" }
};
// Check if a key is an extension
console.log(isExtensionKey("x-custom")); // true
console.log(isExtensionKey("type")); // false
// Extract only extension properties
const extensions = includeXDashProperties(schema);
console.log(extensions);
// { "x-custom-property": "custom-value", "x-vendor-extension": { some: "data" } }
// Remove extension properties
const cleanSchema = omitXDashProperties(schema);
console.log(cleanSchema);
// { type: "object", properties: { name: { type: "string" } } }interface PathReference {
$ref: string;
}
type Refable<T extends {} | undefined> = T | PathReference;
interface Dereferenced<T> {
/** The resolved instance */
instance: T;
/** Name of the referenced item */
name: string;
/** Whether this came from a reference */
fromRef?: boolean;
}
type ExtensionKey = `x-${string}`;
type Extensions = {
[key in ExtensionKey]: any;
};The dereference function throws errors for invalid references:
try {
const resolved = dereference(document, reference);
// Use resolved.instance
} catch (error) {
if (error.message.includes("Circular $ref")) {
// Handle circular reference
} else if (error.message.includes("Invalid Reference")) {
// Handle invalid reference
}
}