Download packages/codemode/src/openapi/spec.ts from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 21.5 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/codemode/src/openapi/spec.ts
- Command line
-
hf download hf://SaylorTwift/opencode/packages/codemode/src/openapi/spec.ts
-
curl -L -o spec.ts https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/codemode/src/openapi/spec.ts
21.5 kB
| import { fromSchemaOpenApi3_0, fromSchemaOpenApi3_1 } from "effect/JsonSchema" | |
| import type { JsonSchema } from "../tool.js" | |
| import { isBlockedMember } from "../tool-runtime.js" | |
| import type { | |
| Body, | |
| Document, | |
| InputField, | |
| OperationInput, | |
| Parsed, | |
| SecurityRequirement, | |
| SecurityScheme, | |
| } from "./types.js" | |
| export const methods = new Set(["get", "put", "post", "delete", "options", "head", "patch", "trace"]) | |
| const parameterLocations = ["path", "query", "header"] as const | |
| const ignoredHeaderParameters = new Set(["accept", "content-type", "authorization"]) | |
| export const isRecord = (value: unknown): value is Record<string, unknown> => | |
| typeof value === "object" && value !== null && !Array.isArray(value) | |
| const asArray = (value: unknown): ReadonlyArray<unknown> => (Array.isArray(value) ? value : []) | |
| export const nonEmptyString = (value: unknown): string | undefined => | |
| typeof value === "string" && value !== "" ? value : undefined | |
| // Guards record lookups keyed by spec- or model-controlled names against | |
| // prototype-inherited values (e.g. a parameter named `toString`). | |
| export const own = <T>(record: Readonly<Record<string, T>>, key: string): T | undefined => | |
| Object.hasOwn(record, key) ? record[key] : undefined | |
| export const resolve = (document: Document, value: unknown): unknown => { | |
| const next = (current: unknown, seen: ReadonlySet<string>): unknown => { | |
| if (!isRecord(current)) return current | |
| const ref = nonEmptyString(current.$ref) | |
| if (ref === undefined || !ref.startsWith("#/") || seen.has(ref)) return current | |
| const target = ref | |
| .slice(2) | |
| .split("/") | |
| .map((segment) => segment.replaceAll("~1", "/").replaceAll("~0", "~")) | |
| .reduce<unknown>((item, segment) => (isRecord(item) ? own(item, segment) : undefined), document) | |
| return target === undefined ? current : next(target, new Set([...seen, ref])) | |
| } | |
| return next(value, new Set()) | |
| } | |
| const projectSchema = (document: Document, value: unknown): JsonSchema => { | |
| if (!isRecord(value)) return {} | |
| const normalized = nonEmptyString(document.openapi)?.startsWith("3.0") | |
| ? fromSchemaOpenApi3_0(value) | |
| : fromSchemaOpenApi3_1(value) | |
| return Object.keys(normalized.definitions).length === 0 | |
| ? normalized.schema | |
| : { ...normalized.schema, $defs: normalized.definitions } | |
| } | |
| export const componentDefinitions = (document: Document): Readonly<Record<string, JsonSchema>> => { | |
| const components = isRecord(document.components) ? document.components : {} | |
| const schemas = isRecord(components.schemas) ? components.schemas : {} | |
| return Object.fromEntries(Object.entries(schemas).map(([name, value]) => [name, projectSchema(document, value)])) | |
| } | |
| const withDefinitions = (schema: JsonSchema, definitions: Readonly<Record<string, JsonSchema>>): JsonSchema => { | |
| if (Object.keys(definitions).length === 0) return schema | |
| const local = isRecord(schema.$defs) ? schema.$defs : {} | |
| return { ...schema, $defs: { ...definitions, ...local } } | |
| } | |
| const isJsonMediaType = (mediaType: string): boolean => { | |
| const normalized = mediaType.split(";")[0]?.trim().toLowerCase() ?? "" | |
| return normalized === "application/json" || normalized.endsWith("+json") | |
| } | |
| const isBinaryMediaType = (document: Document, mediaType: string, value: unknown): boolean => { | |
| const normalized = mediaType.split(";")[0]?.trim().toLowerCase() ?? "" | |
| if (!isJsonMediaType(normalized) && !normalized.startsWith("text/")) return true | |
| if (!isRecord(value)) return false | |
| const schema = resolve(document, value.schema) | |
| return isRecord(schema) && schema.format === "binary" | |
| } | |
| const jsonContent = ( | |
| content: Record<string, unknown>, | |
| ): { readonly mediaType: string; readonly schema: unknown } | undefined => { | |
| const entry = Object.entries(content).find(([mediaType]) => isJsonMediaType(mediaType)) | |
| return entry !== undefined && isRecord(entry[1]) ? { mediaType: entry[0], schema: entry[1].schema } : undefined | |
| } | |
| const isFlattenableObjectBody = ( | |
| schema: unknown, | |
| requestRequired: boolean, | |
| ): schema is Record<string, unknown> & { readonly properties: Record<string, unknown> } => | |
| isRecord(schema) && | |
| requestRequired && | |
| schema.type === "object" && | |
| isRecord(schema.properties) && | |
| schema.additionalProperties === false && | |
| schema.nullable !== true && | |
| schema.allOf === undefined && | |
| schema.anyOf === undefined && | |
| schema.oneOf === undefined | |
| type PlannedField = Omit<InputField, "inputName"> | |
| const operationParameters = ( | |
| document: Document, | |
| pathItem: Record<string, unknown>, | |
| operation: Record<string, unknown>, | |
| ): Parsed<ReadonlyArray<PlannedField>> => { | |
| // Operation-level parameters override path-level ones sharing (location, name). | |
| const declared = new Map< | |
| string, | |
| { readonly name: string; readonly location: string; readonly parameter: Record<string, unknown> } | |
| >() | |
| for (const raw of [...asArray(pathItem.parameters), ...asArray(operation.parameters)]) { | |
| const resolved = resolve(document, raw) | |
| if (!isRecord(resolved)) return { ok: false, reason: "parameter declaration is invalid or unresolved" } | |
| const name = nonEmptyString(resolved.name) | |
| const location = nonEmptyString(resolved.in) | |
| if (name === undefined || location === undefined) | |
| return { ok: false, reason: "parameter declaration is missing name or location" } | |
| declared.set(`${location}:${name}`, { name, location, parameter: resolved }) | |
| } | |
| const unordered: Array<PlannedField> = [] | |
| for (const item of declared.values()) { | |
| const name = item.name | |
| const location = item.location | |
| const resolved = item.parameter | |
| if (location === "cookie") return { ok: false, reason: `cookie parameter '${name}' is not supported` } | |
| if (location !== "path" && location !== "query" && location !== "header") { | |
| return { ok: false, reason: `parameter '${name}' uses unsupported location '${location}'` } | |
| } | |
| if (location === "header" && ignoredHeaderParameters.has(name.toLowerCase())) continue | |
| if (resolved.schema === undefined && resolved.content === undefined) { | |
| return { ok: false, reason: `parameter '${name}' declares neither schema nor content` } | |
| } | |
| if (resolved.content !== undefined) | |
| return { ok: false, reason: `parameter '${name}' uses unsupported content encoding` } | |
| if (resolved.style !== undefined && nonEmptyString(resolved.style) === undefined) { | |
| return { ok: false, reason: `parameter '${name}' has an invalid style` } | |
| } | |
| if (resolved.explode !== undefined && typeof resolved.explode !== "boolean") { | |
| return { ok: false, reason: `parameter '${name}' has an invalid explode value` } | |
| } | |
| if (resolved.allowReserved !== undefined && typeof resolved.allowReserved !== "boolean") { | |
| return { ok: false, reason: `parameter '${name}' has an invalid allowReserved value` } | |
| } | |
| if (resolved.allowReserved === true) | |
| return { ok: false, reason: `parameter '${name}' uses unsupported allowReserved encoding` } | |
| const declaredStyle = nonEmptyString(resolved.style) ?? (location === "query" ? "form" : "simple") | |
| if (location === "query" && declaredStyle !== "form" && declaredStyle !== "deepObject") { | |
| return { ok: false, reason: `query parameter '${name}' uses unsupported style '${declaredStyle}'` } | |
| } | |
| if (location !== "query" && declaredStyle !== "simple") { | |
| return { ok: false, reason: `${location} parameter '${name}' uses unsupported style '${declaredStyle}'` } | |
| } | |
| const style = declaredStyle === "deepObject" ? "deepObject" : declaredStyle === "form" ? "form" : "simple" | |
| const explode = typeof resolved.explode === "boolean" ? resolved.explode : style === "form" | |
| if (style === "deepObject" && !explode) { | |
| return { ok: false, reason: `query parameter '${name}' uses deepObject with explode=false` } | |
| } | |
| const base = projectSchema(document, resolved.schema) | |
| const description = nonEmptyString(resolved.description) | |
| unordered.push({ | |
| name, | |
| location, | |
| required: resolved.required === true || location === "path", | |
| style, | |
| explode, | |
| schema: { | |
| ...base, | |
| ...(base.description === undefined && description !== undefined ? { description } : {}), | |
| }, | |
| }) | |
| } | |
| return { | |
| ok: true, | |
| value: parameterLocations.flatMap((location) => unordered.filter((field) => field.location === location)), | |
| } | |
| } | |
| const operationBody = ( | |
| document: Document, | |
| operation: Record<string, unknown>, | |
| ): Parsed<{ readonly fields: ReadonlyArray<PlannedField>; readonly body: Body | undefined }> => { | |
| const resolved = resolve(document, operation.requestBody) | |
| if (!isRecord(resolved)) return { ok: true, value: { fields: [], body: undefined } } | |
| const content = isRecord(resolved.content) ? resolved.content : {} | |
| const selected = jsonContent(content) | |
| if (selected === undefined) { | |
| return { | |
| ok: false, | |
| reason: `request body has no JSON content (declared: ${Object.keys(content).join(", ") || "none"})`, | |
| } | |
| } | |
| const schema = resolve(document, selected.schema) | |
| const required = resolved.required === true | |
| if (!isFlattenableObjectBody(schema, required)) { | |
| return { | |
| ok: true, | |
| value: { | |
| fields: [ | |
| { | |
| name: "body", | |
| location: "body", | |
| required, | |
| schema: projectSchema(document, selected.schema), | |
| style: undefined, | |
| explode: undefined, | |
| }, | |
| ], | |
| body: { required, mode: "value", mediaType: selected.mediaType }, | |
| }, | |
| } | |
| } | |
| const requiredProperties = new Set( | |
| Array.isArray(schema.required) ? schema.required.filter((item): item is string => typeof item === "string") : [], | |
| ) | |
| return { | |
| ok: true, | |
| value: { | |
| fields: Object.entries(schema.properties).map(([name, value]) => ({ | |
| name, | |
| location: "body" as const, | |
| required: required && requiredProperties.has(name), | |
| schema: projectSchema(document, value), | |
| style: undefined, | |
| explode: undefined, | |
| })), | |
| body: { required, mode: "object", mediaType: selected.mediaType }, | |
| }, | |
| } | |
| } | |
| export const operationInput = ( | |
| document: Document, | |
| pathItem: Record<string, unknown>, | |
| operation: Record<string, unknown>, | |
| ): Parsed<OperationInput> => { | |
| const parameters = operationParameters(document, pathItem, operation) | |
| if (!parameters.ok) return parameters | |
| const requestBody = operationBody(document, operation) | |
| if (!requestBody.ok) return requestBody | |
| const fields = [...parameters.value, ...requestBody.value.fields] | |
| const conflicts = new Set( | |
| [...Map.groupBy(fields, (field) => field.name)] | |
| .filter(([, matches]) => new Set(matches.map((field) => field.location)).size > 1) | |
| .map(([name]) => name), | |
| ) | |
| const used = new Set<string>() | |
| return { | |
| ok: true, | |
| value: { | |
| fields: fields.map((field) => { | |
| const visibleName = isBlockedMember(field.name) ? `${field.name}_2` : field.name | |
| const base = conflicts.has(field.name) ? `${field.location}_${visibleName}` : visibleName | |
| const next = (index: number): string => { | |
| const candidate = index === 1 ? base : `${base}_${index}` | |
| return used.has(candidate) ? next(index + 1) : candidate | |
| } | |
| const inputName = next(1) | |
| used.add(inputName) | |
| return { ...field, inputName } | |
| }), | |
| body: requestBody.value.body, | |
| }, | |
| } | |
| } | |
| export const inputSchema = ( | |
| fields: ReadonlyArray<InputField>, | |
| definitions: Readonly<Record<string, JsonSchema>>, | |
| ): JsonSchema => { | |
| const required = fields.filter((field) => field.required).map((field) => field.inputName) | |
| return withDefinitions( | |
| { | |
| type: "object", | |
| properties: Object.fromEntries(fields.map((field) => [field.inputName, field.schema])), | |
| ...(required.length === 0 ? {} : { required }), | |
| }, | |
| definitions, | |
| ) | |
| } | |
| const successfulResponses = ( | |
| document: Document, | |
| operation: Record<string, unknown>, | |
| ): Parsed<ReadonlyArray<Record<string, unknown>>> => { | |
| if (!isRecord(operation.responses)) return { ok: true, value: [] } | |
| const entries = Object.entries(operation.responses) | |
| const selected = [ | |
| ...entries.filter(([status]) => /^2\d\d$/.test(status)).sort(([a], [b]) => a.localeCompare(b)), | |
| ...entries.filter(([status]) => status.toUpperCase() === "2XX"), | |
| ] | |
| const responses: Array<Record<string, unknown>> = [] | |
| for (const [, value] of selected) { | |
| const resolved = resolve(document, value) | |
| if (!isRecord(resolved) || nonEmptyString(resolved.$ref) !== undefined) { | |
| return { ok: false, reason: "successful response declaration is invalid or unresolved" } | |
| } | |
| responses.push(resolved) | |
| } | |
| return { ok: true, value: responses } | |
| } | |
| export const operationOutput = ( | |
| document: Document, | |
| operation: Record<string, unknown>, | |
| definitions: Readonly<Record<string, JsonSchema>>, | |
| ): Parsed<JsonSchema | undefined> => { | |
| if (operation["x-websocket"] === true) return { ok: false, reason: "WebSocket operations are not supported" } | |
| const responses = successfulResponses(document, operation) | |
| if (!responses.ok) return responses | |
| const streams = responses.value.some( | |
| (response) => | |
| isRecord(response.content) && | |
| Object.keys(response.content).some( | |
| (mediaType) => mediaType.split(";")[0]?.trim().toLowerCase() === "text/event-stream", | |
| ), | |
| ) | |
| if (streams) return { ok: false, reason: "SSE operations are not supported" } | |
| const binary = responses.value.some( | |
| (response) => | |
| isRecord(response.content) && | |
| Object.entries(response.content).some(([mediaType, value]) => isBinaryMediaType(document, mediaType, value)), | |
| ) | |
| if (binary) return { ok: false, reason: "binary responses are not supported" } | |
| const outcomes: Array<JsonSchema> = [] | |
| for (const response of responses.value) { | |
| if (response.content !== undefined && !isRecord(response.content)) return { ok: true, value: undefined } | |
| const content = isRecord(response.content) ? response.content : {} | |
| if (Object.keys(content).length === 0) { | |
| outcomes.push({ type: "null" }) | |
| continue | |
| } | |
| for (const [mediaType, value] of Object.entries(content)) { | |
| if (!isJsonMediaType(mediaType)) { | |
| outcomes.push({ type: "string" }) | |
| continue | |
| } | |
| if (!isRecord(value) || value.schema === undefined) return { ok: true, value: undefined } | |
| outcomes.push(projectSchema(document, value.schema)) | |
| } | |
| } | |
| if (outcomes.length === 0) return { ok: true, value: undefined } | |
| return { | |
| ok: true, | |
| value: withDefinitions(outcomes.length === 1 ? (outcomes[0] ?? {}) : { anyOf: outcomes }, definitions), | |
| } | |
| } | |
| const sanitizeOperationSegment = (raw: string): string => { | |
| const base = | |
| raw | |
| .replaceAll(/[^A-Za-z0-9_$]+/g, "_") | |
| .replace(/^_+|_+$/g, "") | |
| .replace(/^([0-9])/, "_$1") || "operation" | |
| return isBlockedMember(base) ? `${base}_2` : base | |
| } | |
| const fallbackOperationId = (method: string, path: string): string => | |
| [ | |
| method, | |
| ...path | |
| .split("/") | |
| .filter((part) => part !== "") | |
| .flatMap((part) => (part.startsWith("{") && part.endsWith("}") ? ["by", part.slice(1, -1)] : [part])) | |
| .flatMap((part) => part.split(/[^A-Za-z0-9]+/).filter((word) => word !== "")), | |
| ] | |
| .map((word, index) => { | |
| const lower = word.toLowerCase() | |
| return index === 0 ? lower : `${lower.charAt(0).toUpperCase()}${lower.slice(1)}` | |
| }) | |
| .join("") | |
| export const operationPath = ( | |
| method: string, | |
| path: string, | |
| operation: Record<string, unknown>, | |
| used: ReadonlySet<string>, | |
| namespaces: ReadonlySet<string>, | |
| ): ReadonlyArray<string> => { | |
| const raw = nonEmptyString(operation.operationId) | |
| const segments = (raw === undefined ? [fallbackOperationId(method, path)] : raw.split(".")).map( | |
| sanitizeOperationSegment, | |
| ) | |
| if (isOperationPathAvailable(segments, used, namespaces)) return segments | |
| const conflict = segments.slice(0, -1).findIndex((_, index) => used.has(segments.slice(0, index + 1).join("."))) | |
| if (conflict >= 0 && conflict + 1 < segments.length) { | |
| const collapsed = segments.flatMap((segment, index) => { | |
| if (index === conflict) { | |
| const next = segments[index + 1] ?? "" | |
| return [`${segment}${next.charAt(0).toUpperCase()}${next.slice(1)}`] | |
| } | |
| return index === conflict + 1 ? [] : [segment] | |
| }) | |
| if (isOperationPathAvailable(collapsed, used, namespaces)) return collapsed | |
| } | |
| const fallback = segments.join("_") | |
| const next = (index: number): string => { | |
| const candidate = `${fallback}_${index}` | |
| return isOperationPathAvailable([candidate], used, namespaces) ? candidate : next(index + 1) | |
| } | |
| return [next(2)] | |
| } | |
| const isOperationPathAvailable = ( | |
| segments: ReadonlyArray<string>, | |
| used: ReadonlySet<string>, | |
| namespaces: ReadonlySet<string>, | |
| ): boolean => { | |
| const key = segments.join(".") | |
| if (used.has(key) || namespaces.has(key)) return false | |
| return segments.slice(0, -1).every((_, index) => !used.has(segments.slice(0, index + 1).join("."))) | |
| } | |
| export const specServerUrl = (source: Record<string, unknown>): Parsed<string> => { | |
| const server = asArray(source.servers).find(isRecord) | |
| const url = server === undefined ? undefined : nonEmptyString(server.url) | |
| if (url === undefined) return { ok: false, reason: "spec declares no servers; pass baseUrl" } | |
| if (/\{[^{}]+\}/.test(url)) { | |
| return { ok: false, reason: `server URL '${url}' is not an absolute URL; pass baseUrl` } | |
| } | |
| return validateBaseUrl(url) | |
| } | |
| export const validateBaseUrl = (value: string): Parsed<string> => { | |
| if (!/^https?:\/\//i.test(value)) return { ok: false, reason: `server URL '${value}' is not an absolute HTTP(S) URL` } | |
| const url = URL.parse(value) | |
| if (url === null || (url.protocol !== "http:" && url.protocol !== "https:")) { | |
| return { ok: false, reason: `server URL '${value}' is not an absolute HTTP(S) URL` } | |
| } | |
| if (url.search !== "" || url.hash !== "") { | |
| return { ok: false, reason: `server URL '${value}' contains an unsupported query string or fragment` } | |
| } | |
| return { ok: true, value } | |
| } | |
| export const securityRequirements = (value: unknown): Parsed<ReadonlyArray<SecurityRequirement>> => { | |
| if (value === undefined) return { ok: true, value: [] } | |
| if (!Array.isArray(value)) return { ok: false, reason: "security declaration is not an array" } | |
| const requirements: Array<SecurityRequirement> = [] | |
| for (const item of value) { | |
| if (!isRecord(item)) return { ok: false, reason: "security requirement is not an object" } | |
| const requirement = Object.create(null) as Record<string, ReadonlyArray<string>> | |
| for (const [name, scopes] of Object.entries(item)) { | |
| if (!Array.isArray(scopes)) return { ok: false, reason: "security requirement scopes are not string arrays" } | |
| const parsed = scopes.filter((scope): scope is string => typeof scope === "string") | |
| if (parsed.length !== scopes.length) { | |
| return { ok: false, reason: "security requirement scopes are not string arrays" } | |
| } | |
| requirement[name] = parsed | |
| } | |
| requirements.push(requirement) | |
| } | |
| return { ok: true, value: requirements } | |
| } | |
| export const operationSecurityRequirements = ( | |
| value: unknown, | |
| defaults: Parsed<ReadonlyArray<SecurityRequirement>>, | |
| schemes: Readonly<Record<string, SecurityScheme>>, | |
| ): Parsed<ReadonlyArray<SecurityRequirement>> => { | |
| const parsed = value === undefined ? defaults : securityRequirements(value) | |
| if (!parsed.ok) return parsed | |
| const supported = parsed.value.filter((requirement) => | |
| Object.keys(requirement).every((name) => { | |
| const scheme = own(schemes, name) | |
| return scheme !== undefined && !(scheme.type === "apiKey" && scheme.in === "cookie") | |
| }), | |
| ) | |
| if (parsed.value.length === 0 || supported.length > 0) return { ok: true, value: supported } | |
| const names = [...new Set(parsed.value.flatMap((requirement) => Object.keys(requirement)))] | |
| const cookieScheme = names.find((name) => { | |
| const definition = own(schemes, name) | |
| return definition?.type === "apiKey" && definition.in === "cookie" | |
| }) | |
| return { | |
| ok: false, | |
| reason: | |
| cookieScheme === undefined | |
| ? `security requirement references missing or malformed scheme: ${names.join(", ")}` | |
| : `cookie authentication '${cookieScheme}' is not supported`, | |
| } | |
| } | |
| export const securitySchemes = (document: Document): Readonly<Record<string, SecurityScheme>> => { | |
| const components = isRecord(document.components) ? document.components : {} | |
| const declared = isRecord(components.securitySchemes) ? components.securitySchemes : {} | |
| return Object.fromEntries( | |
| Object.entries(declared).flatMap<readonly [string, SecurityScheme]>(([name, value]) => { | |
| const resolved = resolve(document, value) | |
| if (!isRecord(resolved)) return [] | |
| const type = nonEmptyString(resolved.type) | |
| if (type === "apiKey") { | |
| const carrier = nonEmptyString(resolved.in) | |
| const parameter = nonEmptyString(resolved.name) | |
| if (parameter === undefined || (carrier !== "header" && carrier !== "query" && carrier !== "cookie")) return [] | |
| return [[name, { type, name: parameter, in: carrier }] as const] | |
| } | |
| if (type === "http") { | |
| const scheme = nonEmptyString(resolved.scheme)?.toLowerCase() | |
| return scheme === undefined ? [] : [[name, { type, scheme }] as const] | |
| } | |
| if (type === "oauth2" || type === "openIdConnect") return [[name, { type }] as const] | |
| return [] | |
| }), | |
| ) | |
| } | |