/* * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. */ import { ResponseValidationError } from "../models/errors/responsevalidationerror.js"; import { SDKError } from "../models/errors/sdkerror.js"; import { ERR, OK, Result } from "../types/fp.js"; import { matchResponse, matchStatusCode, StatusCodePredicate } from "./http.js"; import { isPlainObject } from "./is-plain-object.js"; export type Encoding = | "jsonl" | "json" | "text" | "bytes" | "stream" | "sse" | "nil" | "fail"; const DEFAULT_CONTENT_TYPES: Record = { jsonl: "application/jsonl", json: "application/json", text: "text/plain", bytes: "application/octet-stream", stream: "application/octet-stream", sse: "text/event-stream", nil: "*", fail: "*", }; type Schema = { parse(raw: unknown): T }; type MatchOptions = { ctype?: string; hdrs?: boolean; key?: string; sseSentinel?: string; }; export type ValueMatcher = MatchOptions & { enc: Encoding; codes: StatusCodePredicate; schema: Schema; }; export type ErrorMatcher = MatchOptions & { enc: Encoding; codes: StatusCodePredicate; schema: Schema; err: true; }; export type FailMatcher = { enc: "fail"; codes: StatusCodePredicate; }; export type Matcher = ValueMatcher | ErrorMatcher | FailMatcher; export function jsonErr( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ErrorMatcher { return { ...options, err: true, enc: "json", codes, schema }; } export function json( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ValueMatcher { return { ...options, enc: "json", codes, schema }; } export function jsonl( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ValueMatcher { return { ...options, enc: "jsonl", codes, schema }; } export function jsonlErr( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ErrorMatcher { return { ...options, err: true, enc: "jsonl", codes, schema }; } export function textErr( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ErrorMatcher { return { ...options, err: true, enc: "text", codes, schema }; } export function text( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ValueMatcher { return { ...options, enc: "text", codes, schema }; } export function bytesErr( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ErrorMatcher { return { ...options, err: true, enc: "bytes", codes, schema }; } export function bytes( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ValueMatcher { return { ...options, enc: "bytes", codes, schema }; } export function streamErr( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ErrorMatcher { return { ...options, err: true, enc: "stream", codes, schema }; } export function stream( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ValueMatcher { return { ...options, enc: "stream", codes, schema }; } export function sseErr( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ErrorMatcher { return { ...options, err: true, enc: "sse", codes, schema }; } export function sse( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ValueMatcher { return { ...options, enc: "sse", codes, schema }; } export function nilErr( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ErrorMatcher { return { ...options, err: true, enc: "nil", codes, schema }; } export function nil( codes: StatusCodePredicate, schema: Schema, options?: MatchOptions, ): ValueMatcher { return { ...options, enc: "nil", codes, schema }; } export function fail(codes: StatusCodePredicate): FailMatcher { return { enc: "fail", codes }; } export type MatchedValue = Matchers extends Matcher[] ? T : never; export type MatchedError = Matchers extends Matcher[] ? E : never; export type MatchFunc = ( response: Response, request: Request, options?: { resultKey?: string; extraFields?: Record }, ) => Promise<[result: Result, raw: unknown]>; export function match( ...matchers: Array> ): MatchFunc { return async function matchFunc( response: Response, request: Request, options?: { resultKey?: string; extraFields?: Record }, ): Promise< [result: Result, raw: unknown] > { let raw: unknown; let matcher: Matcher | undefined; for (const match of matchers) { const { codes } = match; const ctpattern = "ctype" in match ? match.ctype : DEFAULT_CONTENT_TYPES[match.enc]; if (ctpattern && matchResponse(response, codes, ctpattern)) { matcher = match; break; } else if (!ctpattern && matchStatusCode(response, codes)) { matcher = match; break; } } if (!matcher) { return [{ ok: false, error: new SDKError("Unexpected Status or Content-Type", { response, request, body: await response.text().catch(() => ""), }), }, raw]; } const encoding = matcher.enc; let body = ""; switch (encoding) { case "json": body = await response.text(); raw = JSON.parse(body); break; case "jsonl": raw = response.body; break; case "bytes": raw = new Uint8Array(await response.arrayBuffer()); break; case "stream": raw = response.body; break; case "text": body = await response.text(); raw = body; break; case "sse": raw = response.body; break; case "nil": body = await response.text(); raw = undefined; break; case "fail": body = await response.text(); raw = body; break; default: throw new Error( `Unsupported response type: ${encoding satisfies never}`, ); } if (matcher.enc === "fail") { return [{ ok: false, error: new SDKError("API error occurred", { request, response, body }), }, raw]; } const resultKey = matcher.key || options?.resultKey; let data: unknown; if ("err" in matcher) { data = { ...options?.extraFields, ...(matcher.hdrs ? { Headers: unpackHeaders(response.headers) } : null), ...(isPlainObject(raw) ? raw : null), request$: request, response$: response, body$: body, }; } else if (resultKey) { data = { ...options?.extraFields, ...(matcher.hdrs ? { Headers: unpackHeaders(response.headers) } : null), [resultKey]: raw, }; } else if (matcher.hdrs) { data = { ...options?.extraFields, ...(matcher.hdrs ? { Headers: unpackHeaders(response.headers) } : null), ...(isPlainObject(raw) ? raw : null), }; } else { data = raw; } if ("err" in matcher) { const result = safeParseResponse( data, (v: unknown) => matcher.schema.parse(v), "Response validation failed", { request, response, body }, ); return [result.ok ? { ok: false, error: result.value } : result, raw]; } else { return [ safeParseResponse( data, (v: unknown) => matcher.schema.parse(v), "Response validation failed", { request, response, body }, ), raw, ]; } }; } const headerValRE = /, */; /** * Iterates over a Headers object and returns an object with all the header * entries. Values are represented as an array to account for repeated headers. */ export function unpackHeaders(headers: Headers): Record { const out: Record = {}; for (const [k, v] of headers.entries()) { out[k] = v.split(headerValRE); } return out; } function safeParseResponse( rawValue: Inp, fn: (value: Inp) => Out, errorMessage: string, httpMeta: { response: Response; request: Request; body: string }, ): Result { try { return OK(fn(rawValue)); } catch (err) { return ERR( new ResponseValidationError(errorMessage, { cause: err, rawValue, rawMessage: errorMessage, ...httpMeta, }), ); } }