//#region src/ir/sdk-behavior.d.ts /** * SDK Behavior IR — language-agnostic runtime policies for generated SDKs. * * These types capture the "what" of SDK behavior (retry, telemetry, errors, etc.) * while emitters provide the "how" (language-specific mechanism). * * Emitters access these via `ctx.spec.sdk` — always populated, never undefined. */ /** Retry policy — when and how to retry failed requests. */ interface RetryPolicy { /** HTTP status codes that trigger a retry. */ retryableStatusCodes: number[]; /** Maximum number of retry attempts (0 = no retries). */ maxRetries: number; /** Whether to retry on connection errors (DNS failure, TCP reset). */ retryOnConnectionError: boolean; /** Whether to retry on timeout errors. */ retryOnTimeout: boolean; /** Backoff strategy between retries. */ backoff: BackoffStrategy; } /** Exponential backoff configuration. * * Formula: `delay = min(initialDelay * multiplier^attempt, maxDelay)` * With jitter: `delay += delay * jitterFactor * random(0, 1)` */ interface BackoffStrategy { /** Initial delay in seconds before the first retry. */ initialDelay: number; /** Multiplier applied to the delay on each subsequent retry. */ multiplier: number; /** Maximum delay in seconds (cap). */ maxDelay: number; /** Jitter factor (0..1). 0.5 means up to 50% random jitter added. */ jitterFactor: number; } /** Error mapping — status codes to named exception kinds. */ interface ErrorPolicy { /** Map from HTTP status code to a logical error kind name (e.g. 400 → 'BadRequest'). * Emitters append language-specific suffixes (e.g. 'BadRequestException'). */ statusCodeMap: Record; /** Catch-all error kind for 5xx status codes not in the map. */ serverErrorKind: string; /** Catch-all error kind for unrecognized status codes. */ clientErrorKind: string; /** URL template for error documentation. Use {code} as placeholder for the API error code. */ errorDocUrlTemplate?: string; } /** Telemetry — what request metrics to track and send. */ interface TelemetryPolicy { /** Whether telemetry is enabled by default. Users can opt out via constructor param. */ enabledByDefault: boolean; /** Header name for client telemetry data (previous request's ID + latency). */ headerName: string; /** Response header name for the request ID. */ requestIdHeader: string; } /** Pagination behavior defaults. */ interface PaginationPolicy { /** Delay in milliseconds between pages during auto-pagination. 0 = no delay. */ autoPageDelayMs: number; } /** Idempotency auto-generation rules. */ interface IdempotencyPolicy { /** Header name for the idempotency key. */ headerName: string; /** Whether to auto-generate a UUID v4 idempotency key for POST requests when retries > 0. */ autoGenerateForPost: boolean; } /** Logging contract — what to log at which level. */ interface LoggingPolicy { /** Whether structured logging hooks are generated. */ enabled: boolean; /** Log events that the generated client emits. */ events: LogEvent[]; } /** A discrete loggable event in the request lifecycle. */ type LogEvent = 'request.start' | 'request.success' | 'request.retry' | 'request.rate_limited' | 'request.error' | 'request.connection_error'; /** User-Agent construction rules. */ interface UserAgentPolicy { /** Template for the SDK identifier string. * Placeholders: {name} = spec name, {lang} = emitter language, {version} = SDK version. * Each emitter interpolates with its own language string and casing conventions. * Example: '{name} {lang}/{version}' → 'Acme PHP/4.32.0' */ sdkIdentifierTemplate: string; /** Whether to append the runtime/language version (e.g. PHP 8.2, Python 3.12). */ includeRuntimeVersion: boolean; /** Whether to allow app info enrichment via setAppInfo(name, version, url). */ allowAppInfo: boolean; /** AI agent environment variable detection entries. */ aiAgentEnvVars: AiAgentEnvVar[]; } /** Maps an environment variable to an AI agent slug for User-Agent enrichment. */ interface AiAgentEnvVar { /** Environment variable to check (e.g. 'CLAUDE_CODE'). */ envVar: string; /** Agent name to append to User-Agent (e.g. 'ClaudeCode'). */ agentName: string; } /** Guards that validate request params before sending. */ interface RequestGuardPolicy { /** Keys that should be in RequestOptions, not in params. * If detected in the body or query params, the SDK throws an error. */ optionKeys: string[]; } /** Timeout configuration. */ interface TimeoutPolicy { /** Default HTTP request timeout in seconds. */ defaultTimeoutSeconds: number; /** Environment variable name to override the timeout (e.g. 'SDK_REQUEST_TIMEOUT'). */ timeoutEnvVar?: string; } /** Language-agnostic runtime policies for generated SDKs. * Attached to `ApiSpec.sdk` and consumed by emitters via `ctx.spec.sdk`. */ interface SdkBehavior { retry: RetryPolicy; errors: ErrorPolicy; telemetry: TelemetryPolicy; pagination: PaginationPolicy; idempotency: IdempotencyPolicy; logging: LoggingPolicy; userAgent: UserAgentPolicy; requestGuard: RequestGuardPolicy; timeout: TimeoutPolicy; } //#endregion //#region src/ir/types.d.ts /** Authentication scheme extracted from OpenAPI securitySchemes */ type AuthScheme = { kind: 'bearer'; } | { kind: 'apiKey'; in: 'header' | 'query' | 'cookie'; name: string; } | { kind: 'oauth2'; flows: Record; }; /** Root IR node representing the full API surface */ /** A server entry from the OpenAPI servers array */ interface ServerEntry { url: string; description?: string; } /** Root IR node representing the full API surface */ interface ApiSpec { name: string; version: string; description?: string; baseUrl: string; servers?: ServerEntry[]; services: Service[]; models: Model[]; enums: Enum[]; auth?: AuthScheme[]; /** Language-agnostic runtime policies (retry, errors, telemetry, etc.). */ sdk: SdkBehavior; } /** A service groups related operations (maps to an SDK resource class) */ interface Service { name: string; description?: string; operations: Operation[]; } /** A single API operation (maps to an SDK method) */ /** Per-operation security requirement: scheme name → scope list. */ interface SecurityRequirement { schemeName: string; scopes: string[]; } interface Operation { name: string; description?: string; httpMethod: HttpMethod; path: string; pathParams: Parameter[]; queryParams: Parameter[]; headerParams: Parameter[]; cookieParams?: Parameter[]; requestBody?: TypeRef; requestBodyEncoding?: 'json' | 'form-data' | 'form-urlencoded' | 'binary' | 'text'; response: TypeRef; successResponses?: SuccessResponse[]; errors: ErrorResponse[]; pagination?: PaginationMeta; injectIdempotencyKey: boolean; deprecated?: boolean; async?: boolean; /** Per-operation security overrides. When present, overrides the global spec-level security. */ security?: SecurityRequirement[]; /** * Mutually-exclusive parameter groups parsed from `x-mutually-exclusive-parameter-groups`. * Each group represents a logical choice the caller must (or may) make between * several subsets of query/path/header parameters. The grouped parameters remain * in their original arrays (queryParams, pathParams, etc.) for wire-format purposes; * this field adds the grouping metadata so emitters can render sum types. */ parameterGroups?: ParameterGroup[]; } /** * A mutually-exclusive parameter group. The caller must supply exactly one * variant (when `optional` is false) or may omit the group entirely (when true). */ interface ParameterGroup { /** Group name from the extension (e.g. "parent_resource"). */ name: string; /** True when the whole group may be omitted. */ optional: boolean; /** Ordered variants (insertion order matches the spec). */ variants: ParameterGroupVariant[]; } /** * One variant within a mutually-exclusive parameter group. */ interface ParameterGroupVariant { /** Variant name from the extension (e.g. "by_id", "by_external_id"). */ name: string; /** * References to the actual parameter IR nodes. These are the same * objects that live in operation.queryParams / .pathParams / .headerParams -- * shared by identity, not duplicated. */ parameters: Parameter[]; } /** Structured pagination metadata for auto-paging iterator generation */ interface PaginationMeta { strategy: 'cursor' | 'offset' | 'link-header'; param: string; limitParam?: string; dataPath?: string; itemType: TypeRef; } type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head' | 'options' | 'trace'; interface Parameter { name: string; type: TypeRef; required: boolean; description?: string; deprecated?: boolean; default?: unknown; example?: unknown; style?: 'form' | 'simple' | 'label' | 'matrix'; explode?: boolean; } /** Type reference — the core type system of the IR */ type TypeRef = PrimitiveType | ArrayType | ModelRef | EnumRef | UnionType | NullableType | LiteralType | MapType; interface PrimitiveType { kind: 'primitive'; type: 'string' | 'integer' | 'number' | 'boolean' | 'unknown'; format?: string; } interface ArrayType { kind: 'array'; items: TypeRef; } interface ModelRef { kind: 'model'; name: string; } interface EnumRef { kind: 'enum'; name: string; values?: (string | number)[]; } interface LiteralType { kind: 'literal'; value: string | number | boolean | null; } interface UnionType { kind: 'union'; variants: TypeRef[]; discriminator?: { property: string; mapping: Record; }; /** Which OAS composition keyword produced this union. Emitters can use this to * distinguish inheritance (allOf) from exclusive union (oneOf) from open union (anyOf). */ compositionKind?: 'allOf' | 'oneOf' | 'anyOf'; } interface NullableType { kind: 'nullable'; inner: TypeRef; } interface MapType { kind: 'map'; valueType: TypeRef; keyType?: TypeRef; } /** * A generic type parameter on a model. * Example: `DirectoryUser>` * → `{ name: 'TCustomAttributes', default: { kind: 'map', valueType: { kind: 'primitive', type: 'unknown' } } }` */ interface TypeParam { name: string; /** Default type when the param is not specified. */ default?: TypeRef; } /** Model definition (maps to an SDK model/data class) */ interface Model { name: string; description?: string; fields: Field[]; /** Generic type parameters. Empty/undefined for non-generic models. */ typeParams?: TypeParam[]; /** * When set, this model is a discriminated union over allOf+oneOf variants. * Emitters should generate a dispatcher (factory) instead of a regular dataclass. * The `property` field is the discriminator key (e.g. "event"), and `mapping` * maps each discriminator value to the concrete variant model name. */ discriminator?: { property: string; mapping: Record; }; } interface Field { name: string; /** * Optional domain-facing name override. When set, emitters derive the * domain-side identifier (in their own casing) from this instead of `name`, * while serialization still uses `name` for the wire key. Lets an SDK expose * a wire field under a friendlier name (e.g. `connection_type` → `type`) * without changing the API contract. Set via the `fieldHints` config and * honored by any emitter that reads it. */ domainName?: string; type: TypeRef; required: boolean; description?: string; readOnly?: boolean; writeOnly?: boolean; deprecated?: boolean; default?: unknown; example?: unknown; } /** Enum definition */ interface Enum { name: string; values: EnumValue[]; /** Spec-level default value (the value of `default:` on the schema). Optional. */ default?: string | number; } interface EnumValue { name: string; value: string | number; description?: string; deprecated?: boolean; } interface ErrorResponse { statusCode: number; type?: TypeRef; } /** A successful response entry from a 2xx status code */ interface SuccessResponse { statusCode: number; type: TypeRef; } //#endregion //#region src/parser/parse.d.ts /** * A bundled OpenAPI document, post-`$ref` resolution but pre-IR extraction. * * This is the shape the spec author wrote, with external refs inlined and * internal refs left intact. It is intentionally loose (`Record`) so `transformSpec` can reach arbitrary spec extensions without * fighting types — narrow with `as` at use sites. */ type OpenApiDocument = Record; interface ParseOptions { operationIdTransform?: (id: string) => string; schemaNameTransform?: (name: string) => string; /** * Domain-facing field-name overrides, keyed by IR model name then wire field * name: `{ Connection: { connection_type: 'type' } }`. Sets `Field.domainName` * so emitters expose the field under the friendlier name while keeping the * wire key. Language-agnostic — honored by any emitter that reads `domainName`. */ fieldHints?: Record>; /** * Pre-IR overlay applied to the bundled OpenAPI document before any IR * extraction runs. Use this when the upstream spec can't be changed but you * need to patch around a spec quirk that would otherwise emit a breaking SDK * change — e.g. rewriting a path's response `$ref` back to its prior schema, * merging the new fields onto the prior schema, and dropping the fork. * * The function may mutate the document in place and return it, or return a * new object; the returned value is what the rest of oagen sees. * * Runs once, after `$ref` bundling and before schema/operation extraction. * If you need to inspect the resulting IR instead, post-process the * `ApiSpec` returned by `parseSpec`. */ transformSpec?: (spec: OpenApiDocument) => OpenApiDocument; } declare function parseSpec(specPath: string, options?: ParseOptions): Promise; //#endregion //#region src/utils/naming.d.ts declare function toCamelCase(s: string, acronyms?: Set): string; declare function toSnakeCase(s: string): string; //#endregion //#region scripts/smoke/shared.d.ts interface ExchangeProvenance { resolutionTier: 'exact' | 'crud-prefix' | 'fuzzy' | 'manifest'; resolutionConfidence: number; sdkMethodName: string; captureIndex: number; totalCaptures: number; } interface CapturedExchange { operationId: string; service: string; operationName: string; request: { method: string; path: string; queryParams: Record; body: unknown | null; }; response: { status: number; body: unknown | null; }; outcome: 'success' | 'api-error' | 'skipped'; error?: string; /** True when the response status code is not declared in the OpenAPI spec for this operation */ unexpectedStatus?: boolean; /** Status codes declared in the OpenAPI spec (success + error codes) */ expectedStatusCodes?: number[]; durationMs: number; /** How the SDK method was resolved and which capture was selected */ provenance?: ExchangeProvenance; } interface SmokeResults { source: 'raw' | 'spec-baseline' | `sdk-${string}`; timestamp: string; specVersion: string; exchanges: CapturedExchange[]; } interface PlannedOperation { service: string; operation: Operation; } interface PlannedGroup { service: string; operations: PlannedOperation[]; } /** Operations that require complex preconditions and can't be auto-tested */ declare let SKIP_OPERATIONS: Set; /** Services to skip entirely */ declare let SKIP_SERVICES: Set; /** * Map IR service names to SDK property names on the client object. * Empty = use toCamelCase(serviceName) as fallback. */ declare let SERVICE_PROPERTY_MAP: Record; interface SmokeConfig { skipOperations: string[]; skipServices: string[]; servicePriority: Record; servicePropertyMap: Record; } /** Load smoke test configuration from a JSON file */ declare function loadSmokeConfig(configPath?: string): void; /** Return the current smoke config values (for diff.ts to consume) */ declare function getSmokeConfig(): { skipOperations: Set; skipServices: Set; servicePriority: Record; servicePropertyMap: Record; }; declare function planOperations(spec: ApiSpec): PlannedGroup[]; /** * A single wave of operations that can be planned and executed together. * Wave 0 contains parameterless operations. Subsequent waves contain * operations whose path params became resolvable after earlier waves ran. */ interface OperationWave { /** Operations in this wave, with pre-resolved path params */ calls: Array<{ op: Operation; irService: string; pathParams: Record; }>; } /** * Plan operations in waves so that batched runners can execute each wave, * extract IDs from responses, then plan the next wave. * * Returns an iterator that yields one wave at a time. The caller must * execute each wave and populate the IdRegistry before calling next(). * * @param groups - service groups from planOperations() * @param ids - the IdRegistry (mutated by the caller between waves) * @param resolveMethod - optional filter; return null to skip an operation */ declare function planWaves(groups: PlannedGroup[], ids: IdRegistry, resolveMethod?: (op: Operation, irService: string) => boolean): Generator; declare function generateFixtureValue(typeRef: TypeRef, fieldName: string, spec: ApiSpec): unknown; /** Generate a snake_case request body payload from an operation's requestBody type */ declare function generatePayload(op: Operation, spec: ApiSpec): Record | null; /** Generate a camelCase payload for SDK method calls */ declare function generateCamelPayload(op: Operation, spec: ApiSpec): Record | null; /** Generate query params for an operation (required params only, plus limit=1 for lists) */ declare function generateQueryParams(op: Operation, spec: ApiSpec): Record; /** Generate camelCase query params for SDK method calls */ declare function generateCamelQueryParams(op: Operation, spec: ApiSpec): Record; declare class IdRegistry { private ids; set(service: string, field: string, value: string): void; get(service: string, field: string): string | undefined; /** Resolve path parameters for an operation using stored IDs */ resolvePathParams(op: Operation, service: string): Record | null; /** Try to find a param value across all services */ private findAcrossServices; /** * Resolve a path param like "organizationId" by mapping it to the * service that stores that entity's ID (e.g. Organizations.id). * Also handles snake_case suffixed with _id by stripping the suffix * and looking up the pluralized service name. */ private resolveFromParamName; /** * Find a value by looking for any service name that ends with the given suffix. * e.g. suffix "Users" matches "UserManagementUsers.id" */ private findByServiceSuffix; /** * Last-resort fallback: use the spec's example or default value for a parameter. * Many path params (e.g. actionName, slug, type) have example values in the spec. */ private resolveFromExample; /** * Extract IDs from a response body and store them. * @param isTopLevel - true for operations without path params (their `id` belongs to this service). * false for sub-resource operations (their `id` is a child entity, don't overwrite). */ extractAndStore(service: string, responseBody: unknown, isTopLevel?: boolean): void; /** Extract fields ending in _id or Id and store them for cross-service resolution */ private extractNestedIds; } /** Get the set of status codes declared in the spec for an operation (success + errors) */ declare function getExpectedStatusCodes(op: Operation): number[]; /** Check if a status code is unexpected (not declared in the spec) */ declare function isUnexpectedStatus(status: number, op: Operation): boolean; declare function resolvePath(op: Operation, pathParams: Record): string; declare function delay(ms: number): Promise; declare function parseCliArgs(): { spec: string; sdkPath?: string; smokeConfig?: string; }; //#endregion export { CapturedExchange, ExchangeProvenance, IdRegistry, OperationWave, PlannedGroup, PlannedOperation, SERVICE_PROPERTY_MAP, SKIP_OPERATIONS, SKIP_SERVICES, SmokeConfig, SmokeResults, delay, generateCamelPayload, generateCamelQueryParams, generateFixtureValue, generatePayload, generateQueryParams, getExpectedStatusCodes, getSmokeConfig, isUnexpectedStatus, loadSmokeConfig, parseCliArgs, parseSpec, planOperations, planWaves, resolvePath, toCamelCase, toSnakeCase }; //# sourceMappingURL=smoke.d.mts.map