293 lines
6.7 KiB
TypeScript
293 lines
6.7 KiB
TypeScript
/**
|
|
* Configuration schema and types for AI provider code generation.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
import * as Data from "effect/Data"
|
|
import type * as Path from "effect/Path"
|
|
import * as Schema from "effect/Schema"
|
|
|
|
/**
|
|
* A text replacement to apply to generated code.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export class Replacement extends Schema.Class<Replacement>("Replacement")({
|
|
from: Schema.String,
|
|
to: Schema.String
|
|
}) {}
|
|
|
|
/**
|
|
* Structured spec source configuration for Stainless stats indirection.
|
|
*
|
|
* @category schemas
|
|
* @since 4.0.0
|
|
*/
|
|
export const SpecSourceConfig = Schema.Struct({
|
|
type: Schema.Literal("stainless-stats"),
|
|
statsUrl: Schema.String
|
|
})
|
|
|
|
/**
|
|
* Configuration for AI provider code generation.
|
|
*
|
|
* **Example** (Decoding a codegen configuration)
|
|
*
|
|
* ```ts
|
|
* import * as Config from "@effect/ai-codegen/Config"
|
|
* import { Schema } from "effect"
|
|
*
|
|
* const config = Schema.decodeUnknownSync(Config.CodegenConfig)({
|
|
* spec: "https://example.com/openapi.json",
|
|
* output: "Generated.ts",
|
|
* name: "MyClient"
|
|
* })
|
|
*
|
|
* console.log(config.spec)
|
|
* // "https://example.com/openapi.json"
|
|
* ```
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export class CodegenConfig extends Schema.Class<CodegenConfig>("CodegenConfig")({
|
|
spec: Schema.Union([Schema.String, SpecSourceConfig]),
|
|
output: Schema.String,
|
|
name: Schema.optional(Schema.String),
|
|
typeOnly: Schema.optional(Schema.Boolean),
|
|
header: Schema.optional(Schema.String),
|
|
patches: Schema.optional(Schema.Array(Schema.String)),
|
|
replacements: Schema.optional(Schema.Array(Replacement)),
|
|
excludeAnnotations: Schema.optional(Schema.Array(Schema.String)),
|
|
disableAdditionalProperties: Schema.optional(Schema.Boolean)
|
|
}) {
|
|
/**
|
|
* Get the client name, defaulting to "Client" if not specified.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
get clientName(): string {
|
|
return this.name ?? "Client"
|
|
}
|
|
|
|
/**
|
|
* Check if type-only generation is enabled.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
get isTypeOnly(): boolean {
|
|
return this.typeOnly ?? false
|
|
}
|
|
|
|
/**
|
|
* Get the list of patch files/strings to apply.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
get patchList(): ReadonlyArray<string> {
|
|
return this.patches ?? []
|
|
}
|
|
|
|
/**
|
|
* Get the list of text replacements to apply.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
get replacementList(): ReadonlyArray<Replacement> {
|
|
return this.replacements ?? []
|
|
}
|
|
|
|
/**
|
|
* Get the header content to prepend to generated files.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
get headerContent(): string | undefined {
|
|
return this.header
|
|
}
|
|
|
|
/**
|
|
* Get the list of annotation keys to exclude from generated code.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
get excludeAnnotationsList(): ReadonlyArray<string> | undefined {
|
|
return this.excludeAnnotations
|
|
}
|
|
|
|
/**
|
|
* Check if additionalProperties should be forced to false on all object schemas.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
get shouldDisableAdditionalProperties(): boolean {
|
|
return this.disableAdditionalProperties ?? false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Represents the source of an OpenAPI specification.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export type SpecSource = SpecSource.Url | SpecSource.File | SpecSource.StainlessStats
|
|
|
|
/**
|
|
* Namespace containing the supported OpenAPI specification source variants.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
export declare namespace SpecSource {
|
|
/**
|
|
* A URL-based spec source.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export interface Url {
|
|
readonly _tag: "Url"
|
|
readonly url: string
|
|
}
|
|
|
|
/**
|
|
* A file-based spec source.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export interface File {
|
|
readonly _tag: "File"
|
|
readonly path: string
|
|
}
|
|
|
|
/**
|
|
* Stainless SDK stats.yml indirection - fetches stats file and extracts openapi_spec_url.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export interface StainlessStats {
|
|
readonly _tag: "StainlessStats"
|
|
readonly statsUrl: string
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructors and utilities for `SpecSource`.
|
|
*
|
|
* **Example** (Creating spec sources)
|
|
*
|
|
* ```ts
|
|
* import * as Config from "@effect/ai-codegen/Config"
|
|
*
|
|
* // Create a URL-based source
|
|
* const urlSource = Config.SpecSource.Url("https://example.com/openapi.json")
|
|
*
|
|
* // Create a file-based source
|
|
* const fileSource = Config.SpecSource.File("/path/to/spec.json")
|
|
* ```
|
|
*
|
|
* @category constructors
|
|
* @since 4.0.0
|
|
*/
|
|
export const SpecSource = {
|
|
/**
|
|
* Create a URL-based spec source.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
Url: (url: string): SpecSource => ({ _tag: "Url", url }),
|
|
|
|
/**
|
|
* Create a file-based spec source.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
File: (path: string): SpecSource => ({ _tag: "File", path }),
|
|
|
|
/**
|
|
* Create a Stainless stats-based spec source.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
StainlessStats: (statsUrl: string): SpecSource => ({ _tag: "StainlessStats", statsUrl }),
|
|
|
|
/**
|
|
* Parse a spec string into a `SpecSource`.
|
|
* URLs (http:// or https://) become `Url`, otherwise `File`.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
fromString: (spec: string, packagePath: string, pathService: Path.Path): SpecSource => {
|
|
if (spec.startsWith("http://") || spec.startsWith("https://")) {
|
|
return SpecSource.Url(spec)
|
|
}
|
|
return SpecSource.File(pathService.join(packagePath, spec))
|
|
},
|
|
|
|
/**
|
|
* Parse a spec config (string or object) into a `SpecSource`.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
fromConfig: (
|
|
spec: string | { readonly type: string; readonly statsUrl?: string },
|
|
packagePath: string,
|
|
pathService: Path.Path
|
|
): SpecSource => {
|
|
if (typeof spec === "string") {
|
|
return SpecSource.fromString(spec, packagePath, pathService)
|
|
}
|
|
if (spec.type === "stainless-stats" && spec.statsUrl) {
|
|
return SpecSource.StainlessStats(spec.statsUrl)
|
|
}
|
|
throw new Error(`Unknown spec type: ${spec.type}`)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Error when parsing a codegen configuration file fails.
|
|
*
|
|
* **Example** (Creating a config parse error)
|
|
*
|
|
* ```ts
|
|
* import * as Config from "@effect/ai-codegen/Config"
|
|
*
|
|
* const error = new Config.ConfigParseError({
|
|
* path: "/path/to/codegen.json",
|
|
* cause: new Error("Invalid JSON")
|
|
* })
|
|
* ```
|
|
*
|
|
* @category errors
|
|
* @since 4.0.0
|
|
*/
|
|
export class ConfigParseError extends Data.TaggedError("ConfigParseError")<{
|
|
readonly path: string
|
|
readonly cause: unknown
|
|
}> {}
|
|
|
|
/**
|
|
* Error when a codegen configuration file is not found.
|
|
*
|
|
* **Example** (Creating a config not found error)
|
|
*
|
|
* ```ts
|
|
* import * as Config from "@effect/ai-codegen/Config"
|
|
*
|
|
* const error = new Config.ConfigNotFoundError({
|
|
* provider: "openai",
|
|
* expectedPath: "/path/to/packages/ai/openai/codegen.json"
|
|
* })
|
|
* ```
|
|
*
|
|
* @category errors
|
|
* @since 4.0.0
|
|
*/
|
|
export class ConfigNotFoundError extends Data.TaggedError("ConfigNotFoundError")<{
|
|
readonly provider: string
|
|
readonly expectedPath: string
|
|
}> {}
|