TypeScript models and utilities for building OpenAPI 3.x specification documents with fluent DSL builder pattern
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Support for OpenAPI specification extensions (x-* properties) with validation and management utilities.
Core types for working with OpenAPI specification extensions.
/**
* Extension name pattern - must start with 'x-'
*/
type IExtensionName = `x-${string}`;
/**
* Extension value type - can be any value
*/
type IExtensionType = any;
/**
* Interface for objects supporting specification extensions
*/
interface ISpecificationExtension {
[extensionName: IExtensionName]: IExtensionType;
}Implementation class for managing OpenAPI specification extensions.
/**
* Specification extension management class
*/
class SpecificationExtension implements ISpecificationExtension {
[extensionName: IExtensionName]: any;
/**
* Validate if an extension name follows the x- pattern
* @param extensionName - Extension name to validate
* @returns True if extension name is valid (starts with 'x-')
*/
static isValidExtension(extensionName: string): boolean;
/**
* Get an extension value by name
* @param extensionName - Extension name (must start with 'x-')
* @returns Extension value or null if not found
* @throws Error if extension name is invalid
*/
getExtension(extensionName: string): any;
/**
* Add an extension with validation
* @param extensionName - Extension name (must start with 'x-')
* @param payload - Extension value
* @throws Error if extension name is invalid
*/
addExtension(extensionName: string, payload: any): void;
/**
* List all extension names on this object
* @returns Array of extension names
*/
listExtensions(): string[];
}Usage Examples:
import { SpecificationExtension } from "openapi3-ts";
// Create extension manager
const extensions = new SpecificationExtension();
// Add extensions
extensions.addExtension("x-custom-field", "custom value");
extensions.addExtension("x-rate-limit", { requests: 100, window: "1m" });
extensions.addExtension("x-internal-id", 12345);
// Get extension values
const customField = extensions.getExtension("x-custom-field");
console.log(customField); // "custom value"
const rateLimit = extensions.getExtension("x-rate-limit");
console.log(rateLimit); // { requests: 100, window: "1m" }
// List all extensions
const extensionNames = extensions.listExtensions();
console.log(extensionNames); // ["x-custom-field", "x-rate-limit", "x-internal-id"]
// Validate extension names
console.log(SpecificationExtension.isValidExtension("x-valid")); // true
console.log(SpecificationExtension.isValidExtension("invalid")); // false
// Error handling
try {
extensions.addExtension("invalid-name", "value");
} catch (error) {
console.error(error.message); // "Invalid specification extension..."
}Helper functions for working with extensions on any OpenAPI object.
/**
* Get an extension value from any object supporting extensions
* @param obj - Object with potential extensions
* @param extensionName - Extension name (must start with 'x-')
* @returns Extension value or undefined
*/
function getExtension(
obj: ISpecificationExtension | undefined,
extensionName: string
): any;
/**
* Add an extension to any object supporting extensions
* @param obj - Object to add extension to
* @param extensionName - Extension name (must start with 'x-')
* @param extension - Extension value
*/
function addExtension(
obj: ISpecificationExtension | undefined,
extensionName: string,
extension: any
): void;Usage Examples:
import { oas30, getExtension, addExtension } from "openapi3-ts";
// Create OpenAPI objects
const schema: oas30.SchemaObject = {
type: "object",
properties: {
id: { type: "integer" }
}
};
const operation: oas30.OperationObject = {
summary: "Get item",
responses: {
"200": { description: "Success" }
}
};
// Add extensions using utility functions
addExtension(schema, "x-database-table", "items");
addExtension(schema, "x-validation-level", "strict");
addExtension(operation, "x-rate-limit", 1000);
addExtension(operation, "x-cached", true);
// Get extensions using utility functions
const tableName = getExtension(schema, "x-database-table");
console.log(tableName); // "items"
const rateLimit = getExtension(operation, "x-rate-limit");
console.log(rateLimit); // 1000
// Extensions are accessible as properties too
console.log(schema["x-validation-level"]); // "strict"
console.log(operation["x-cached"]); // trueimport { oas30 } from "openapi3-ts";
// Vendor-specific extensions
const spec = oas30.OpenApiBuilder.create()
.addTitle("API with Extensions")
.addVersion("1.0.0")
.getSpec();
// Add vendor extensions to the root document
spec["x-amazon-apigateway-cors"] = {
allowOrigins: ["*"],
allowMethods: ["GET", "POST"],
allowHeaders: ["Content-Type", "Authorization"]
};
spec["x-swagger-ui"] = {
theme: "dark",
displayRequestDuration: true
};
// Schema-level extensions for code generation
const userSchema: oas30.SchemaObject = {
type: "object",
required: ["id", "name"],
properties: {
id: { type: "integer", format: "int64" },
name: { type: "string" },
email: { type: "string", format: "email" }
}
};
// Add code generation hints
userSchema["x-go-type"] = "User";
userSchema["x-java-class"] = "com.example.User";
userSchema["x-database-table"] = "users";
userSchema["x-tags"] = ["entity", "user"];
// Operation-level extensions for infrastructure
const getUserOperation: oas30.OperationObject = {
summary: "Get user by ID",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "integer" }
}
],
responses: {
"200": {
description: "User found",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/User" }
}
}
}
}
};
// Add infrastructure extensions
getUserOperation["x-rate-limit"] = { requests: 100, window: "1m" };
getUserOperation["x-cache-ttl"] = 300; // 5 minutes
getUserOperation["x-auth-required"] = true;
getUserOperation["x-permissions"] = ["user:read"];import { SpecificationExtension, ISpecificationExtension } from "openapi3-ts";
// Helper class for managing typed extensions
class ExtensionManager {
private extensions: ISpecificationExtension = {};
addRateLimit(requests: number, window: string): void {
this.extensions["x-rate-limit"] = { requests, window };
}
addCacheSettings(ttl: number, strategy: string): void {
this.extensions["x-cache"] = { ttl, strategy };
}
addVendorConfig(vendor: string, config: object): void {
const extensionName = `x-${vendor}-config` as any;
if (SpecificationExtension.isValidExtension(extensionName)) {
this.extensions[extensionName] = config;
}
}
applyTo(target: ISpecificationExtension): void {
Object.keys(this.extensions).forEach(key => {
if (SpecificationExtension.isValidExtension(key)) {
target[key as any] = this.extensions[key as any];
}
});
}
getExtensions(): ISpecificationExtension {
return { ...this.extensions };
}
}
// Usage
const extManager = new ExtensionManager();
extManager.addRateLimit(1000, "1h");
extManager.addCacheSettings(600, "lru");
extManager.addVendorConfig("aws", {
region: "us-east-1",
timeout: 30000
});
// Apply to OpenAPI objects
const operation: oas30.OperationObject = {
summary: "Test operation",
responses: { "200": { description: "Success" } }
};
extManager.applyTo(operation);
console.log(operation["x-rate-limit"]); // { requests: 1000, window: "1h" }
console.log(operation["x-aws-config"]); // { region: "us-east-1", timeout: 30000 }import { oas30, SpecificationExtension } from "openapi3-ts";
// Extend builder with extension support
class ExtendedOpenApiBuilder extends oas30.OpenApiBuilder {
addExtension(extensionName: string, value: any): ExtendedOpenApiBuilder {
if (SpecificationExtension.isValidExtension(extensionName)) {
this.rootDoc[extensionName as any] = value;
} else {
throw new Error(`Invalid extension name: ${extensionName}`);
}
return this;
}
addSchemaWithExtensions(
name: string,
schema: oas30.SchemaObject,
extensions: { [key: string]: any }
): ExtendedOpenApiBuilder {
// Add extensions to schema
Object.keys(extensions).forEach(key => {
if (SpecificationExtension.isValidExtension(key)) {
(schema as any)[key] = extensions[key];
}
});
return this.addSchema(name, schema) as ExtendedOpenApiBuilder;
}
addOperationWithExtensions(
path: string,
method: string,
operation: oas30.OperationObject,
extensions: { [key: string]: any }
): ExtendedOpenApiBuilder {
// Add extensions to operation
Object.keys(extensions).forEach(key => {
if (SpecificationExtension.isValidExtension(key)) {
(operation as any)[key] = extensions[key];
}
});
const pathItem: oas30.PathItemObject = {
[method]: operation
};
return this.addPath(path, pathItem) as ExtendedOpenApiBuilder;
}
}
// Usage
const spec = new ExtendedOpenApiBuilder()
.addTitle("Extended API")
.addVersion("1.0.0")
.addExtension("x-api-id", "api-12345")
.addExtension("x-build-info", {
version: "1.2.3",
buildTime: new Date().toISOString(),
gitCommit: "abc123"
})
.addSchemaWithExtensions("User", {
type: "object",
properties: {
id: { type: "integer" },
name: { type: "string" }
}
}, {
"x-database-table": "users",
"x-java-class": "com.example.User"
})
.addOperationWithExtensions("/users", "get", {
summary: "List users",
responses: {
"200": { description: "Success" }
}
}, {
"x-rate-limit": { requests: 100, window: "1m" },
"x-cache-ttl": 300
})
.getSpec();
console.log(spec["x-api-id"]); // "api-12345"
console.log(spec.components?.schemas?.User?.["x-database-table"]); // "users"// Good extension names
const goodExtensions = [
"x-internal-id", // kebab-case
"x-rate-limit", // descriptive
"x-vendor-config", // vendor prefix
"x-code-gen-options", // purpose clear
];
// Poor extension names
const poorExtensions = [
"x-data", // too generic
"x-temp", // unclear purpose
"x-customField", // camelCase (prefer kebab)
"x-123", // starts with number
];
// Validation helper
function isGoodExtensionName(name: string): boolean {
return SpecificationExtension.isValidExtension(name) &&
name.length > 2 &&
/^x-[a-z][a-z0-9-]*$/.test(name);
}import { ISpecificationExtension } from "openapi3-ts";
// Define typed extension interfaces
interface RateLimitExtension {
"x-rate-limit": {
requests: number;
window: string;
};
}
interface CacheExtension {
"x-cache-ttl": number;
}
// Utility types for extension combinations
type WithRateLimit<T> = T & RateLimitExtension;
type WithCache<T> = T & CacheExtension;
type WithBoth<T> = T & RateLimitExtension & CacheExtension;
// Type-safe extension helpers
function addRateLimit<T extends ISpecificationExtension>(
obj: T,
requests: number,
window: string
): WithRateLimit<T> {
(obj as any)["x-rate-limit"] = { requests, window };
return obj as WithRateLimit<T>;
}
function addCache<T extends ISpecificationExtension>(
obj: T,
ttl: number
): WithCache<T> {
(obj as any)["x-cache-ttl"] = ttl;
return obj as WithCache<T>;
}
// Usage with type safety
import { oas30 } from "openapi3-ts";
let operation: oas30.OperationObject = {
summary: "Get user",
responses: { "200": { description: "Success" } }
};
// Chain extensions with type safety
operation = addCache(addRateLimit(operation, 100, "1m"), 300);
// TypeScript knows about the extensions
console.log(operation["x-rate-limit"].requests); // 100
console.log(operation["x-cache-ttl"]); // 300Install with Tessl CLI
npx tessl i tessl/npm-openapi3-ts