Files
flyer-crawler.projectium.com/src/config/apiVersions.ts
Torben Sorensen f10c6c0cd6
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 17m56s
Complete ADR-008 Phase 2
2026-01-27 11:06:09 -08:00

184 lines
5.4 KiB
TypeScript

// src/config/apiVersions.ts
/**
* @file API version constants, types, and configuration.
* Implements ADR-008 Phase 2: API Versioning Infrastructure.
*
* This module provides centralized version definitions used by:
* - Version detection middleware (apiVersion.middleware.ts)
* - Deprecation headers middleware (deprecation.middleware.ts)
* - Versioned router factory (versioned.ts)
*
* @see docs/architecture/api-versioning-infrastructure.md
*
* @example
* ```typescript
* import {
* CURRENT_API_VERSION,
* VERSION_CONFIGS,
* isValidApiVersion,
* } from './apiVersions';
*
* // Check if a version is supported
* if (isValidApiVersion('v1')) {
* const config = VERSION_CONFIGS.v1;
* console.log(`v1 status: ${config.status}`);
* }
* ```
*/
// --- Type Definitions ---
/**
* All API versions as a const tuple for type derivation.
* Add new versions here when introducing them.
*/
export const API_VERSIONS = ['v1', 'v2'] as const;
/**
* Union type of supported API versions.
* Currently: 'v1' | 'v2'
*/
export type ApiVersion = (typeof API_VERSIONS)[number];
/**
* Version lifecycle status.
* - 'active': Version is fully supported and recommended
* - 'deprecated': Version works but clients should migrate (deprecation headers sent)
* - 'sunset': Version is scheduled for removal or already removed
*/
export type VersionStatus = 'active' | 'deprecated' | 'sunset';
/**
* Deprecation information for an API version.
* Follows RFC 8594 (Sunset Header) and draft-ietf-httpapi-deprecation-header.
*
* Used by deprecation middleware to set appropriate HTTP headers:
* - `Deprecation: true` (draft-ietf-httpapi-deprecation-header)
* - `Sunset: <date>` (RFC 8594)
* - `Link: <url>; rel="successor-version"` (RFC 8288)
*/
export interface VersionDeprecation {
/** Indicates if this version is deprecated (maps to Deprecation header) */
deprecated: boolean;
/** ISO 8601 date string when the version will be sunset (maps to Sunset header) */
sunsetDate?: string;
/** The version clients should migrate to (maps to Link rel="successor-version") */
successorVersion?: ApiVersion;
/** Human-readable message explaining the deprecation (for documentation/logs) */
message?: string;
}
/**
* Complete configuration for an API version.
* Combines version identifier, lifecycle status, and deprecation details.
*/
export interface VersionConfig {
/** The version identifier (e.g., 'v1') */
version: ApiVersion;
/** Current lifecycle status of this version */
status: VersionStatus;
/** ISO 8601 date when the version will be sunset (RFC 8594) - convenience field */
sunsetDate?: string;
/** The version clients should migrate to - convenience field */
successorVersion?: ApiVersion;
}
// --- Constants ---
/**
* The current/latest stable API version.
* New clients should use this version.
*/
export const CURRENT_API_VERSION: ApiVersion = 'v1';
/**
* The default API version for requests without explicit version.
* Used when version cannot be detected from the request path.
*/
export const DEFAULT_VERSION: ApiVersion = 'v1';
/**
* Array of all supported API versions.
* Used for validation and enumeration.
*/
export const SUPPORTED_VERSIONS: readonly ApiVersion[] = API_VERSIONS;
/**
* Configuration map for all API versions.
* Provides lifecycle status and deprecation information for each version.
*
* To mark v1 as deprecated (example for future use):
* @example
* ```typescript
* VERSION_CONFIGS.v1 = {
* version: 'v1',
* status: 'deprecated',
* sunsetDate: '2027-01-01T00:00:00Z',
* successorVersion: 'v2',
* };
* ```
*/
export const VERSION_CONFIGS: Record<ApiVersion, VersionConfig> = {
v1: {
version: 'v1',
status: 'active',
// No deprecation info - v1 is the current active version
},
v2: {
version: 'v2',
status: 'active',
// v2 is defined for infrastructure readiness but not yet implemented
},
};
// --- Utility Functions ---
/**
* Type guard to check if a string is a valid ApiVersion.
*
* @param value - The string to check
* @returns True if the string is a valid API version
*
* @example
* ```typescript
* const userInput = 'v1';
* if (isValidApiVersion(userInput)) {
* // userInput is now typed as ApiVersion
* const config = VERSION_CONFIGS[userInput];
* }
* ```
*/
export function isValidApiVersion(value: string): value is ApiVersion {
return API_VERSIONS.includes(value as ApiVersion);
}
/**
* Check if a version is deprecated.
*
* @param version - The API version to check
* @returns True if the version status is 'deprecated'
*/
export function isVersionDeprecated(version: ApiVersion): boolean {
return VERSION_CONFIGS[version].status === 'deprecated';
}
/**
* Get deprecation information for a version.
* Constructs a VersionDeprecation object from the version config.
*
* @param version - The API version to get deprecation info for
* @returns VersionDeprecation object with current deprecation state
*/
export function getVersionDeprecation(version: ApiVersion): VersionDeprecation {
const config = VERSION_CONFIGS[version];
return {
deprecated: config.status === 'deprecated',
sunsetDate: config.sunsetDate,
successorVersion: config.successorVersion,
message:
config.status === 'deprecated'
? `API ${version} is deprecated${config.sunsetDate ? ` and will be sunset on ${config.sunsetDate}` : ''}${config.successorVersion ? `. Please migrate to ${config.successorVersion}.` : '.'}`
: undefined,
};
}