This commit is contained in:
Dax Raad 2025-12-07 19:03:20 -05:00
commit c35c15a3ac
27 changed files with 6177 additions and 5573 deletions

View file

@ -324,7 +324,7 @@
"name": "@opencode-ai/sdk", "name": "@opencode-ai/sdk",
"version": "1.0.134", "version": "1.0.134",
"devDependencies": { "devDependencies": {
"@hey-api/openapi-ts": "https://pkg.pr.new/hey-api/openapi-ts/@hey-api/openapi-ts@3047", "@hey-api/openapi-ts": "0.88.1",
"@tsconfig/node22": "catalog:", "@tsconfig/node22": "catalog:",
"@types/node": "catalog:", "@types/node": "catalog:",
"@typescript/native-preview": "catalog:", "@typescript/native-preview": "catalog:",
@ -851,11 +851,11 @@
"@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="], "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="],
"@hey-api/codegen-core": ["@hey-api/codegen-core@https://pkg.pr.new/hey-api/openapi-ts/@hey-api/codegen-core@55ef27994740ba0af0bc8db17b3e71bfb5c11714", { "peerDependencies": { "typescript": ">=5.5.3" } }], "@hey-api/codegen-core": ["@hey-api/codegen-core@0.3.3", "", { "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-vArVDtrvdzFewu1hnjUm4jX1NBITlSCeO81EdWq676MxQbyxsGcDPAgohaSA+Wvr4HjPSvsg2/1s2zYxUtXebg=="],
"@hey-api/json-schema-ref-parser": ["@hey-api/json-schema-ref-parser@1.2.2", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.1", "lodash": "^4.17.21" } }, "sha512-oS+5yAdwnK20lSeFO1d53Ku+yaGCsY8PcrmSq2GtSs3bsBfRnHAbpPKSVzQcaxAOrzj5NB+f34WhZglVrNayBA=="], "@hey-api/json-schema-ref-parser": ["@hey-api/json-schema-ref-parser@1.2.2", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.1", "lodash": "^4.17.21" } }, "sha512-oS+5yAdwnK20lSeFO1d53Ku+yaGCsY8PcrmSq2GtSs3bsBfRnHAbpPKSVzQcaxAOrzj5NB+f34WhZglVrNayBA=="],
"@hey-api/openapi-ts": ["@hey-api/openapi-ts@https://pkg.pr.new/hey-api/openapi-ts/@hey-api/openapi-ts@3047", { "dependencies": { "@hey-api/codegen-core": "https://pkg.pr.new/hey-api/openapi-ts/@hey-api/codegen-core@55ef27994740ba0af0bc8db17b3e71bfb5c11714", "@hey-api/json-schema-ref-parser": "1.2.2", "ansi-colors": "4.1.3", "c12": "3.3.2", "color-support": "1.1.3", "commander": "14.0.2", "open": "11.0.0", "semver": "7.7.2" }, "peerDependencies": { "typescript": ">=5.5.3" }, "bin": { "openapi-ts": "./bin/run.js" } }], "@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.88.1", "", { "dependencies": { "@hey-api/codegen-core": "^0.3.3", "@hey-api/json-schema-ref-parser": "1.2.2", "ansi-colors": "4.1.3", "c12": "3.3.2", "color-support": "1.1.3", "commander": "14.0.2", "open": "11.0.0", "semver": "7.7.2" }, "peerDependencies": { "typescript": ">=5.5.3" }, "bin": { "openapi-ts": "bin/run.js" } }, "sha512-x/nDTupOnV9VuSeNIiJpgIpc915GHduhyseJeMTnI0JMsXaObmpa0rgPr3ASVEYMLgpvqozIEG1RTOOnal6zLQ=="],
"@hono/standard-validator": ["@hono/standard-validator@0.1.5", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-EIyZPPwkyLn6XKwFj5NBEWHXhXbgmnVh2ceIFo5GO7gKI9WmzTjPDKnppQB0KrqKeAkq3kpoW4SIbu5X1dgx3w=="], "@hono/standard-validator": ["@hono/standard-validator@0.1.5", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-EIyZPPwkyLn6XKwFj5NBEWHXhXbgmnVh2ceIFo5GO7gKI9WmzTjPDKnppQB0KrqKeAkq3kpoW4SIbu5X1dgx3w=="],

View file

@ -235,9 +235,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const abort = () => const abort = () =>
sdk.client.session.abort({ sdk.client.session.abort({
path: { sessionID: session.id!,
id: session.id!,
},
}) })
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
@ -329,21 +327,19 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
session.prompt.set([{ type: "text", content: "", start: 0, end: 0 }], 0) session.prompt.set([{ type: "text", content: "", start: 0, end: 0 }], 0)
sdk.client.session.prompt({ sdk.client.session.prompt({
path: { id: existing.id }, sessionID: existing.id,
body: { agent: local.agent.current()!.name,
agent: local.agent.current()!.name, model: {
model: { modelID: local.model.current()!.id,
modelID: local.model.current()!.id, providerID: local.model.current()!.provider.id,
providerID: local.model.current()!.provider.id,
},
parts: [
{
type: "text",
text,
},
...attachmentParts,
],
}, },
parts: [
{
type: "text",
text,
},
...attachmentParts,
],
}) })
} }

View file

@ -74,12 +74,10 @@ export const Terminal = (props: TerminalProps) => {
term.onResize(async (size) => { term.onResize(async (size) => {
if (ws && ws.readyState === WebSocket.OPEN) { if (ws && ws.readyState === WebSocket.OPEN) {
await sdk.client.pty.update({ await sdk.client.pty.update({
path: { id: local.pty.id }, ptyID: local.pty.id,
body: { size: {
size: { cols: size.cols,
cols: size.cols, rows: size.rows,
rows: size.rows,
},
}, },
}) })
} }
@ -100,12 +98,10 @@ export const Terminal = (props: TerminalProps) => {
ws.addEventListener("open", () => { ws.addEventListener("open", () => {
console.log("WebSocket connected") console.log("WebSocket connected")
sdk.client.pty.update({ sdk.client.pty.update({
path: { id: local.pty.id }, ptyID: local.pty.id,
body: { size: {
size: { cols: term.cols,
cols: term.cols, rows: term.rows,
rows: term.rows,
},
}, },
}) })
}) })

View file

@ -1,4 +1,4 @@
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/client" import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { createGlobalEmitter } from "@solid-primitives/event-bus" import { createGlobalEmitter } from "@solid-primitives/event-bus"
import { onCleanup } from "solid-js" import { onCleanup } from "solid-js"

View file

@ -12,7 +12,7 @@ import type {
FileDiff, FileDiff,
Todo, Todo,
SessionStatus, SessionStatus,
} from "@opencode-ai/sdk" } from "@opencode-ai/sdk/v2"
import { createStore, produce, reconcile } from "solid-js/store" import { createStore, produce, reconcile } from "solid-js/store"
import { Binary } from "@opencode-ai/util/binary" import { Binary } from "@opencode-ai/util/binary"
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"

View file

@ -1,7 +1,7 @@
import { createStore, produce, reconcile } from "solid-js/store" import { createStore, produce, reconcile } from "solid-js/store"
import { batch, createEffect, createMemo } from "solid-js" import { batch, createEffect, createMemo } from "solid-js"
import { uniqueBy } from "remeda" import { uniqueBy } from "remeda"
import type { FileContent, FileNode, Model, Provider, File as FileStatus } from "@opencode-ai/sdk" import type { FileContent, FileNode, Model, Provider, File as FileStatus } from "@opencode-ai/sdk/v2"
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { useSDK } from "./sdk" import { useSDK } from "./sdk"
import { useSync } from "./sync" import { useSync } from "./sync"
@ -257,7 +257,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const load = async (path: string) => { const load = async (path: string) => {
const relativePath = relative(path) const relativePath = relative(path)
sdk.client.file.read({ query: { path: relativePath } }).then((x) => { sdk.client.file.read({ path: relativePath }).then((x) => {
setStore( setStore(
"node", "node",
relativePath, relativePath,
@ -305,7 +305,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
} }
const list = async (path: string) => { const list = async (path: string) => {
return sdk.client.file.list({ query: { path: path + "/" } }).then((x) => { return sdk.client.file.list({ path: path + "/" }).then((x) => {
setStore( setStore(
"node", "node",
produce((draft) => { produce((draft) => {
@ -318,10 +318,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
}) })
} }
const searchFiles = (query: string) => const searchFiles = (query: string) => sdk.client.find.files({ query, dirs: "false" }).then((x) => x.data!)
sdk.client.find.files({ query: { query, dirs: "false" } }).then((x) => x.data!)
const searchFilesAndDirectories = (query: string) => const searchFilesAndDirectories = (query: string) =>
sdk.client.find.files({ query: { query, dirs: "true" } }).then((x) => x.data!) sdk.client.find.files({ query, dirs: "true" }).then((x) => x.data!)
sdk.event.listen((e) => { sdk.event.listen((e) => {
const event = e.details const event = e.details

View file

@ -1,4 +1,4 @@
import { createOpencodeClient, type Event } from "@opencode-ai/sdk/client" import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2/client"
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { createGlobalEmitter } from "@solid-primitives/event-bus" import { createGlobalEmitter } from "@solid-primitives/event-bus"
import { onCleanup } from "solid-js" import { onCleanup } from "solid-js"

View file

@ -5,7 +5,7 @@ import { useSync } from "./sync"
import { makePersisted } from "@solid-primitives/storage" import { makePersisted } from "@solid-primitives/storage"
import { TextSelection } from "./local" import { TextSelection } from "./local"
import { pipe, sumBy } from "remeda" import { pipe, sumBy } from "remeda"
import { AssistantMessage, UserMessage } from "@opencode-ai/sdk" import { AssistantMessage, UserMessage } from "@opencode-ai/sdk/v2"
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { base64Encode } from "@/utils" import { base64Encode } from "@/utils"
import { useSDK } from "./sdk" import { useSDK } from "./sdk"
@ -198,7 +198,7 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
all: createMemo(() => Object.values(store.terminals.all)), all: createMemo(() => Object.values(store.terminals.all)),
active: createMemo(() => store.terminals.active), active: createMemo(() => store.terminals.active),
new() { new() {
sdk.client.pty.create({ body: { title: `Terminal ${store.terminals.all.length + 1}` } }).then((pty) => { sdk.client.pty.create({ title: `Terminal ${store.terminals.all.length + 1}` }).then((pty) => {
const id = pty.data?.id const id = pty.data?.id
if (!id) return if (!id) return
setStore("terminals", "all", [ setStore("terminals", "all", [
@ -214,8 +214,9 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
update(pty: Partial<LocalPTY> & { id: string }) { update(pty: Partial<LocalPTY> & { id: string }) {
setStore("terminals", "all", (x) => x.map((x) => (x.id === pty.id ? { ...x, ...pty } : x))) setStore("terminals", "all", (x) => x.map((x) => (x.id === pty.id ? { ...x, ...pty } : x)))
sdk.client.pty.update({ sdk.client.pty.update({
path: { id: pty.id }, ptyID: pty.id,
body: { title: pty.title, size: pty.cols && pty.rows ? { rows: pty.rows, cols: pty.cols } : undefined }, title: pty.title,
size: pty.cols && pty.rows ? { rows: pty.rows, cols: pty.cols } : undefined,
}) })
}, },
async clone(id: string) { async clone(id: string) {
@ -223,9 +224,7 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
const pty = store.terminals.all[index] const pty = store.terminals.all[index]
if (!pty) return if (!pty) return
const clone = await sdk.client.pty.create({ const clone = await sdk.client.pty.create({
body: { title: pty.title,
title: pty.title,
},
}) })
if (!clone.data) return if (!clone.data) return
setStore("terminals", "all", index, { setStore("terminals", "all", index, {
@ -252,7 +251,7 @@ export const { use: useSession, provider: SessionProvider } = createSimpleContex
setStore("terminals", "active", previous) setStore("terminals", "active", previous)
} }
}) })
await sdk.client.pty.remove({ path: { id } }) await sdk.client.pty.remove({ ptyID: id })
}, },
move(id: string, to: number) { move(id: string, to: number) {
const index = store.terminals.all.findIndex((f) => f.id === id) const index = store.terminals.all.findIndex((f) => f.id === id)

View file

@ -28,7 +28,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
status: () => sdk.client.session.status().then((x) => setStore("session_status", x.data!)), status: () => sdk.client.session.status().then((x) => setStore("session_status", x.data!)),
config: () => sdk.client.config.get().then((x) => setStore("config", x.data!)), config: () => sdk.client.config.get().then((x) => setStore("config", x.data!)),
changes: () => sdk.client.file.status().then((x) => setStore("changes", x.data!)), changes: () => sdk.client.file.status().then((x) => setStore("changes", x.data!)),
node: () => sdk.client.file.list({ query: { path: "/" } }).then((x) => setStore("node", x.data!)), node: () => sdk.client.file.list({ path: "/" }).then((x) => setStore("node", x.data!)),
} }
Promise.all(Object.values(load).map((p) => p())).then(() => setStore("ready", true)) Promise.all(Object.values(load).map((p) => p())).then(() => setStore("ready", true))
@ -49,10 +49,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
}, },
async sync(sessionID: string, _isRetry = false) { async sync(sessionID: string, _isRetry = false) {
const [session, messages, todo, diff] = await Promise.all([ const [session, messages, todo, diff] = await Promise.all([
sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }), sdk.client.session.get({ sessionID }, { throwOnError: true }),
sdk.client.session.messages({ path: { id: sessionID }, query: { limit: 100 } }), sdk.client.session.messages({ sessionID, limit: 100 }),
sdk.client.session.todo({ path: { id: sessionID } }), sdk.client.session.todo({ sessionID }),
sdk.client.session.diff({ path: { id: sessionID } }), sdk.client.session.diff({ sessionID }),
]) ])
setStore( setStore(
produce((draft) => { produce((draft) => {

View file

@ -13,7 +13,7 @@ import { Collapsible } from "@opencode-ai/ui/collapsible"
import { DiffChanges } from "@opencode-ai/ui/diff-changes" import { DiffChanges } from "@opencode-ai/ui/diff-changes"
import { getFilename } from "@opencode-ai/util/path" import { getFilename } from "@opencode-ai/util/path"
import { Select } from "@opencode-ai/ui/select" import { Select } from "@opencode-ai/ui/select"
import { Session } from "@opencode-ai/sdk/client" import { Session } from "@opencode-ai/sdk/v2/client"
export default function Layout(props: ParentProps) { export default function Layout(props: ParentProps) {
const navigate = useNavigate() const navigate = useNavigate()

View file

@ -19,7 +19,7 @@
"dist" "dist"
], ],
"devDependencies": { "devDependencies": {
"@hey-api/openapi-ts": "https://pkg.pr.new/hey-api/openapi-ts/@hey-api/openapi-ts@3047", "@hey-api/openapi-ts": "0.88.1",
"@tsconfig/node22": "catalog:", "@tsconfig/node22": "catalog:",
"@types/node": "catalog:", "@types/node": "catalog:",
"typescript": "catalog:", "typescript": "catalog:",

View file

@ -10,8 +10,6 @@ import { createClient } from "@hey-api/openapi-ts"
await $`bun dev generate > ${dir}/openapi.json`.cwd(path.resolve(dir, "../../opencode")) await $`bun dev generate > ${dir}/openapi.json`.cwd(path.resolve(dir, "../../opencode"))
await $`rm -rf src/gen`
await createClient({ await createClient({
input: "./openapi.json", input: "./openapi.json",
output: { output: {
@ -40,5 +38,6 @@ await createClient({
}) })
await $`bun prettier --write src/gen` await $`bun prettier --write src/gen`
await $`bun prettier --write src/v2`
await $`rm -rf dist` await $`rm -rf dist`
await $`bun tsc` await $`bun tsc`

View file

@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import { type ClientOptions, type Config, createClient, createConfig } from './client/index.js'; import { type ClientOptions, type Config, createClient, createConfig } from "./client/index.js"
import type { ClientOptions as ClientOptions2 } from './types.gen.js'; import type { ClientOptions as ClientOptions2 } from "./types.gen.js"
/** /**
* The `createClientConfig()` function will be called on client initialization * The `createClientConfig()` function will be called on client initialization
@ -11,6 +11,8 @@ import type { ClientOptions as ClientOptions2 } from './types.gen.js';
* `setConfig()`. This is useful for example if you're using Next.js * `setConfig()`. This is useful for example if you're using Next.js
* to ensure your client always has the correct values. * to ensure your client always has the correct values.
*/ */
export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T>; export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (
override?: Config<ClientOptions & T>,
) => Config<Required<ClientOptions> & T>
export const client = createClient(createConfig<ClientOptions2>({ baseUrl: 'http://localhost:4096' })); export const client = createClient(createConfig<ClientOptions2>({ baseUrl: "http://localhost:4096" }))

View file

@ -1,14 +1,9 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import { createSseClient } from '../core/serverSentEvents.gen.js'; import { createSseClient } from "../core/serverSentEvents.gen.js"
import type { HttpMethod } from '../core/types.gen.js'; import type { HttpMethod } from "../core/types.gen.js"
import { getValidRequestBody } from '../core/utils.gen.js'; import { getValidRequestBody } from "../core/utils.gen.js"
import type { import type { Client, Config, RequestOptions, ResolvedRequestOptions } from "./types.gen.js"
Client,
Config,
RequestOptions,
ResolvedRequestOptions,
} from './types.gen.js';
import { import {
buildUrl, buildUrl,
createConfig, createConfig,
@ -17,29 +12,24 @@ import {
mergeConfigs, mergeConfigs,
mergeHeaders, mergeHeaders,
setAuthParams, setAuthParams,
} from './utils.gen.js'; } from "./utils.gen.js"
type ReqInit = Omit<RequestInit, 'body' | 'headers'> & { type ReqInit = Omit<RequestInit, "body" | "headers"> & {
body?: any; body?: any
headers: ReturnType<typeof mergeHeaders>; headers: ReturnType<typeof mergeHeaders>
}; }
export const createClient = (config: Config = {}): Client => { export const createClient = (config: Config = {}): Client => {
let _config = mergeConfigs(createConfig(), config); let _config = mergeConfigs(createConfig(), config)
const getConfig = (): Config => ({ ..._config }); const getConfig = (): Config => ({ ..._config })
const setConfig = (config: Config): Config => { const setConfig = (config: Config): Config => {
_config = mergeConfigs(_config, config); _config = mergeConfigs(_config, config)
return getConfig(); return getConfig()
}; }
const interceptors = createInterceptors< const interceptors = createInterceptors<Request, Response, unknown, ResolvedRequestOptions>()
Request,
Response,
unknown,
ResolvedRequestOptions
>();
const beforeRequest = async (options: RequestOptions) => { const beforeRequest = async (options: RequestOptions) => {
const opts = { const opts = {
@ -48,254 +38,241 @@ export const createClient = (config: Config = {}): Client => {
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
headers: mergeHeaders(_config.headers, options.headers), headers: mergeHeaders(_config.headers, options.headers),
serializedBody: undefined, serializedBody: undefined,
}; }
if (opts.security) { if (opts.security) {
await setAuthParams({ await setAuthParams({
...opts, ...opts,
security: opts.security, security: opts.security,
}); })
} }
if (opts.requestValidator) { if (opts.requestValidator) {
await opts.requestValidator(opts); await opts.requestValidator(opts)
} }
if (opts.body !== undefined && opts.bodySerializer) { if (opts.body !== undefined && opts.bodySerializer) {
opts.serializedBody = opts.bodySerializer(opts.body); opts.serializedBody = opts.bodySerializer(opts.body)
} }
// remove Content-Type header if body is empty to avoid sending invalid requests // remove Content-Type header if body is empty to avoid sending invalid requests
if (opts.body === undefined || opts.serializedBody === '') { if (opts.body === undefined || opts.serializedBody === "") {
opts.headers.delete('Content-Type'); opts.headers.delete("Content-Type")
} }
const url = buildUrl(opts); const url = buildUrl(opts)
return { opts, url }; return { opts, url }
}; }
const request: Client['request'] = async (options) => { const request: Client["request"] = async (options) => {
// @ts-expect-error // @ts-expect-error
const { opts, url } = await beforeRequest(options); const { opts, url } = await beforeRequest(options)
const requestInit: ReqInit = { const requestInit: ReqInit = {
redirect: 'follow', redirect: "follow",
...opts, ...opts,
body: getValidRequestBody(opts), body: getValidRequestBody(opts),
}; }
let request = new Request(url, requestInit); let request = new Request(url, requestInit)
for (const fn of interceptors.request.fns) { for (const fn of interceptors.request.fns) {
if (fn) { if (fn) {
request = await fn(request, opts); request = await fn(request, opts)
} }
} }
// fetch must be assigned here, otherwise it would throw the error: // fetch must be assigned here, otherwise it would throw the error:
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
const _fetch = opts.fetch!; const _fetch = opts.fetch!
let response: Response; let response: Response
try { try {
response = await _fetch(request); response = await _fetch(request)
} catch (error) { } catch (error) {
// Handle fetch exceptions (AbortError, network errors, etc.) // Handle fetch exceptions (AbortError, network errors, etc.)
let finalError = error; let finalError = error
for (const fn of interceptors.error.fns) { for (const fn of interceptors.error.fns) {
if (fn) { if (fn) {
finalError = (await fn( finalError = (await fn(error, undefined as any, request, opts)) as unknown
error,
undefined as any,
request,
opts,
)) as unknown;
} }
} }
finalError = finalError || ({} as unknown); finalError = finalError || ({} as unknown)
if (opts.throwOnError) { if (opts.throwOnError) {
throw finalError; throw finalError
} }
// Return error response // Return error response
return opts.responseStyle === 'data' return opts.responseStyle === "data"
? undefined ? undefined
: { : {
error: finalError, error: finalError,
request, request,
response: undefined as any, response: undefined as any,
}; }
} }
for (const fn of interceptors.response.fns) { for (const fn of interceptors.response.fns) {
if (fn) { if (fn) {
response = await fn(response, request, opts); response = await fn(response, request, opts)
} }
} }
const result = { const result = {
request, request,
response, response,
}; }
if (response.ok) { if (response.ok) {
const parseAs = const parseAs =
(opts.parseAs === 'auto' (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json"
? getParseAs(response.headers.get('Content-Type'))
: opts.parseAs) ?? 'json';
if ( if (response.status === 204 || response.headers.get("Content-Length") === "0") {
response.status === 204 || let emptyData: any
response.headers.get('Content-Length') === '0'
) {
let emptyData: any;
switch (parseAs) { switch (parseAs) {
case 'arrayBuffer': case "arrayBuffer":
case 'blob': case "blob":
case 'text': case "text":
emptyData = await response[parseAs](); emptyData = await response[parseAs]()
break; break
case 'formData': case "formData":
emptyData = new FormData(); emptyData = new FormData()
break; break
case 'stream': case "stream":
emptyData = response.body; emptyData = response.body
break; break
case 'json': case "json":
default: default:
emptyData = {}; emptyData = {}
break; break
} }
return opts.responseStyle === 'data' return opts.responseStyle === "data"
? emptyData ? emptyData
: { : {
data: emptyData, data: emptyData,
...result, ...result,
}; }
} }
let data: any; let data: any
switch (parseAs) { switch (parseAs) {
case 'arrayBuffer': case "arrayBuffer":
case 'blob': case "blob":
case 'formData': case "formData":
case 'json': case "json":
case 'text': case "text":
data = await response[parseAs](); data = await response[parseAs]()
break; break
case 'stream': case "stream":
return opts.responseStyle === 'data' return opts.responseStyle === "data"
? response.body ? response.body
: { : {
data: response.body, data: response.body,
...result, ...result,
}; }
} }
if (parseAs === 'json') { if (parseAs === "json") {
if (opts.responseValidator) { if (opts.responseValidator) {
await opts.responseValidator(data); await opts.responseValidator(data)
} }
if (opts.responseTransformer) { if (opts.responseTransformer) {
data = await opts.responseTransformer(data); data = await opts.responseTransformer(data)
} }
} }
return opts.responseStyle === 'data' return opts.responseStyle === "data"
? data ? data
: { : {
data, data,
...result, ...result,
}; }
} }
const textError = await response.text(); const textError = await response.text()
let jsonError: unknown; let jsonError: unknown
try { try {
jsonError = JSON.parse(textError); jsonError = JSON.parse(textError)
} catch { } catch {
// noop // noop
} }
const error = jsonError ?? textError; const error = jsonError ?? textError
let finalError = error; let finalError = error
for (const fn of interceptors.error.fns) { for (const fn of interceptors.error.fns) {
if (fn) { if (fn) {
finalError = (await fn(error, response, request, opts)) as string; finalError = (await fn(error, response, request, opts)) as string
} }
} }
finalError = finalError || ({} as string); finalError = finalError || ({} as string)
if (opts.throwOnError) { if (opts.throwOnError) {
throw finalError; throw finalError
} }
// TODO: we probably want to return error and improve types // TODO: we probably want to return error and improve types
return opts.responseStyle === 'data' return opts.responseStyle === "data"
? undefined ? undefined
: { : {
error: finalError, error: finalError,
...result, ...result,
}; }
}; }
const makeMethodFn = const makeMethodFn = (method: Uppercase<HttpMethod>) => (options: RequestOptions) => request({ ...options, method })
(method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
request({ ...options, method });
const makeSseFn = const makeSseFn = (method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
(method: Uppercase<HttpMethod>) => async (options: RequestOptions) => { const { opts, url } = await beforeRequest(options)
const { opts, url } = await beforeRequest(options); return createSseClient({
return createSseClient({ ...opts,
...opts, body: opts.body as BodyInit | null | undefined,
body: opts.body as BodyInit | null | undefined, headers: opts.headers as unknown as Record<string, string>,
headers: opts.headers as unknown as Record<string, string>, method,
method, onRequest: async (url, init) => {
onRequest: async (url, init) => { let request = new Request(url, init)
let request = new Request(url, init); for (const fn of interceptors.request.fns) {
for (const fn of interceptors.request.fns) { if (fn) {
if (fn) { request = await fn(request, opts)
request = await fn(request, opts);
}
} }
return request; }
}, return request
url, },
}); url,
}; })
}
return { return {
buildUrl, buildUrl,
connect: makeMethodFn('CONNECT'), connect: makeMethodFn("CONNECT"),
delete: makeMethodFn('DELETE'), delete: makeMethodFn("DELETE"),
get: makeMethodFn('GET'), get: makeMethodFn("GET"),
getConfig, getConfig,
head: makeMethodFn('HEAD'), head: makeMethodFn("HEAD"),
interceptors, interceptors,
options: makeMethodFn('OPTIONS'), options: makeMethodFn("OPTIONS"),
patch: makeMethodFn('PATCH'), patch: makeMethodFn("PATCH"),
post: makeMethodFn('POST'), post: makeMethodFn("POST"),
put: makeMethodFn('PUT'), put: makeMethodFn("PUT"),
request, request,
setConfig, setConfig,
sse: { sse: {
connect: makeSseFn('CONNECT'), connect: makeSseFn("CONNECT"),
delete: makeSseFn('DELETE'), delete: makeSseFn("DELETE"),
get: makeSseFn('GET'), get: makeSseFn("GET"),
head: makeSseFn('HEAD'), head: makeSseFn("HEAD"),
options: makeSseFn('OPTIONS'), options: makeSseFn("OPTIONS"),
patch: makeSseFn('PATCH'), patch: makeSseFn("PATCH"),
post: makeSseFn('POST'), post: makeSseFn("POST"),
put: makeSseFn('PUT'), put: makeSseFn("PUT"),
trace: makeSseFn('TRACE'), trace: makeSseFn("TRACE"),
}, },
trace: makeMethodFn('TRACE'), trace: makeMethodFn("TRACE"),
} as Client; } as Client
}; }

View file

@ -1,15 +1,15 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
export type { Auth } from '../core/auth.gen.js'; export type { Auth } from "../core/auth.gen.js"
export type { QuerySerializerOptions } from '../core/bodySerializer.gen.js'; export type { QuerySerializerOptions } from "../core/bodySerializer.gen.js"
export { export {
formDataBodySerializer, formDataBodySerializer,
jsonBodySerializer, jsonBodySerializer,
urlSearchParamsBodySerializer, urlSearchParamsBodySerializer,
} from '../core/bodySerializer.gen.js'; } from "../core/bodySerializer.gen.js"
export { buildClientParams } from '../core/params.gen.js'; export { buildClientParams } from "../core/params.gen.js"
export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen.js'; export { serializeQueryKeyValue } from "../core/queryKeySerializer.gen.js"
export { createClient } from './client.gen.js'; export { createClient } from "./client.gen.js"
export type { export type {
Client, Client,
ClientOptions, ClientOptions,
@ -21,5 +21,5 @@ export type {
ResolvedRequestOptions, ResolvedRequestOptions,
ResponseStyle, ResponseStyle,
TDataShape, TDataShape,
} from './types.gen.js'; } from "./types.gen.js"
export { createConfig, mergeHeaders } from './utils.gen.js'; export { createConfig, mergeHeaders } from "./utils.gen.js"

View file

@ -1,39 +1,33 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import type { Auth } from '../core/auth.gen.js'; import type { Auth } from "../core/auth.gen.js"
import type { import type { ServerSentEventsOptions, ServerSentEventsResult } from "../core/serverSentEvents.gen.js"
ServerSentEventsOptions, import type { Client as CoreClient, Config as CoreConfig } from "../core/types.gen.js"
ServerSentEventsResult, import type { Middleware } from "./utils.gen.js"
} from '../core/serverSentEvents.gen.js';
import type {
Client as CoreClient,
Config as CoreConfig,
} from '../core/types.gen.js';
import type { Middleware } from './utils.gen.js';
export type ResponseStyle = 'data' | 'fields'; export type ResponseStyle = "data" | "fields"
export interface Config<T extends ClientOptions = ClientOptions> export interface Config<T extends ClientOptions = ClientOptions>
extends Omit<RequestInit, 'body' | 'headers' | 'method'>, extends Omit<RequestInit, "body" | "headers" | "method">,
CoreConfig { CoreConfig {
/** /**
* Base URL for all requests made by this client. * Base URL for all requests made by this client.
*/ */
baseUrl?: T['baseUrl']; baseUrl?: T["baseUrl"]
/** /**
* Fetch API implementation. You can use this option to provide a custom * Fetch API implementation. You can use this option to provide a custom
* fetch instance. * fetch instance.
* *
* @default globalThis.fetch * @default globalThis.fetch
*/ */
fetch?: typeof fetch; fetch?: typeof fetch
/** /**
* Please don't use the Fetch client for Next.js applications. The `next` * Please don't use the Fetch client for Next.js applications. The `next`
* options won't have any effect. * options won't have any effect.
* *
* Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead.
*/ */
next?: never; next?: never
/** /**
* Return the response data parsed in a specified format. By default, `auto` * Return the response data parsed in a specified format. By default, `auto`
* will infer the appropriate method from the `Content-Type` response header. * will infer the appropriate method from the `Content-Type` response header.
@ -42,170 +36,140 @@ export interface Config<T extends ClientOptions = ClientOptions>
* *
* @default 'auto' * @default 'auto'
*/ */
parseAs?: parseAs?: "arrayBuffer" | "auto" | "blob" | "formData" | "json" | "stream" | "text"
| 'arrayBuffer'
| 'auto'
| 'blob'
| 'formData'
| 'json'
| 'stream'
| 'text';
/** /**
* Should we return only data or multiple fields (data, error, response, etc.)? * Should we return only data or multiple fields (data, error, response, etc.)?
* *
* @default 'fields' * @default 'fields'
*/ */
responseStyle?: ResponseStyle; responseStyle?: ResponseStyle
/** /**
* Throw an error instead of returning it in the response? * Throw an error instead of returning it in the response?
* *
* @default false * @default false
*/ */
throwOnError?: T['throwOnError']; throwOnError?: T["throwOnError"]
} }
export interface RequestOptions< export interface RequestOptions<
TData = unknown, TData = unknown,
TResponseStyle extends ResponseStyle = 'fields', TResponseStyle extends ResponseStyle = "fields",
ThrowOnError extends boolean = boolean, ThrowOnError extends boolean = boolean,
Url extends string = string, Url extends string = string,
> extends Config<{ > extends Config<{
responseStyle: TResponseStyle; responseStyle: TResponseStyle
throwOnError: ThrowOnError; throwOnError: ThrowOnError
}>, }>,
Pick< Pick<
ServerSentEventsOptions<TData>, ServerSentEventsOptions<TData>,
| 'onSseError' "onSseError" | "onSseEvent" | "sseDefaultRetryDelay" | "sseMaxRetryAttempts" | "sseMaxRetryDelay"
| 'onSseEvent'
| 'sseDefaultRetryDelay'
| 'sseMaxRetryAttempts'
| 'sseMaxRetryDelay'
> { > {
/** /**
* Any body that you want to add to your request. * Any body that you want to add to your request.
* *
* {@link https://developer.mozilla.org/docs/Web/API/fetch#body} * {@link https://developer.mozilla.org/docs/Web/API/fetch#body}
*/ */
body?: unknown; body?: unknown
path?: Record<string, unknown>; path?: Record<string, unknown>
query?: Record<string, unknown>; query?: Record<string, unknown>
/** /**
* Security mechanism(s) to use for the request. * Security mechanism(s) to use for the request.
*/ */
security?: ReadonlyArray<Auth>; security?: ReadonlyArray<Auth>
url: Url; url: Url
} }
export interface ResolvedRequestOptions< export interface ResolvedRequestOptions<
TResponseStyle extends ResponseStyle = 'fields', TResponseStyle extends ResponseStyle = "fields",
ThrowOnError extends boolean = boolean, ThrowOnError extends boolean = boolean,
Url extends string = string, Url extends string = string,
> extends RequestOptions<unknown, TResponseStyle, ThrowOnError, Url> { > extends RequestOptions<unknown, TResponseStyle, ThrowOnError, Url> {
serializedBody?: string; serializedBody?: string
} }
export type RequestResult< export type RequestResult<
TData = unknown, TData = unknown,
TError = unknown, TError = unknown,
ThrowOnError extends boolean = boolean, ThrowOnError extends boolean = boolean,
TResponseStyle extends ResponseStyle = 'fields', TResponseStyle extends ResponseStyle = "fields",
> = ThrowOnError extends true > = ThrowOnError extends true
? Promise< ? Promise<
TResponseStyle extends 'data' TResponseStyle extends "data"
? TData extends Record<string, unknown> ? TData extends Record<string, unknown>
? TData[keyof TData] ? TData[keyof TData]
: TData : TData
: { : {
data: TData extends Record<string, unknown> data: TData extends Record<string, unknown> ? TData[keyof TData] : TData
? TData[keyof TData] request: Request
: TData; response: Response
request: Request;
response: Response;
} }
> >
: Promise< : Promise<
TResponseStyle extends 'data' TResponseStyle extends "data"
? ? (TData extends Record<string, unknown> ? TData[keyof TData] : TData) | undefined
| (TData extends Record<string, unknown>
? TData[keyof TData]
: TData)
| undefined
: ( : (
| { | {
data: TData extends Record<string, unknown> data: TData extends Record<string, unknown> ? TData[keyof TData] : TData
? TData[keyof TData] error: undefined
: TData;
error: undefined;
} }
| { | {
data: undefined; data: undefined
error: TError extends Record<string, unknown> error: TError extends Record<string, unknown> ? TError[keyof TError] : TError
? TError[keyof TError]
: TError;
} }
) & { ) & {
request: Request; request: Request
response: Response; response: Response
} }
>; >
export interface ClientOptions { export interface ClientOptions {
baseUrl?: string; baseUrl?: string
responseStyle?: ResponseStyle; responseStyle?: ResponseStyle
throwOnError?: boolean; throwOnError?: boolean
} }
type MethodFn = < type MethodFn = <
TData = unknown, TData = unknown,
TError = unknown, TError = unknown,
ThrowOnError extends boolean = false, ThrowOnError extends boolean = false,
TResponseStyle extends ResponseStyle = 'fields', TResponseStyle extends ResponseStyle = "fields",
>( >(
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'>, options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">,
) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>; ) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>
type SseFn = < type SseFn = <
TData = unknown, TData = unknown,
TError = unknown, TError = unknown,
ThrowOnError extends boolean = false, ThrowOnError extends boolean = false,
TResponseStyle extends ResponseStyle = 'fields', TResponseStyle extends ResponseStyle = "fields",
>( >(
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'>, options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method">,
) => Promise<ServerSentEventsResult<TData, TError>>; ) => Promise<ServerSentEventsResult<TData, TError>>
type RequestFn = < type RequestFn = <
TData = unknown, TData = unknown,
TError = unknown, TError = unknown,
ThrowOnError extends boolean = false, ThrowOnError extends boolean = false,
TResponseStyle extends ResponseStyle = 'fields', TResponseStyle extends ResponseStyle = "fields",
>( >(
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'> & options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, "method"> &
Pick< Pick<Required<RequestOptions<TData, TResponseStyle, ThrowOnError>>, "method">,
Required<RequestOptions<TData, TResponseStyle, ThrowOnError>>, ) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>
'method'
>,
) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>;
type BuildUrlFn = < type BuildUrlFn = <
TData extends { TData extends {
body?: unknown; body?: unknown
path?: Record<string, unknown>; path?: Record<string, unknown>
query?: Record<string, unknown>; query?: Record<string, unknown>
url: string; url: string
}, },
>( >(
options: TData & Options<TData>, options: TData & Options<TData>,
) => string; ) => string
export type Client = CoreClient< export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn, SseFn> & {
RequestFn, interceptors: Middleware<Request, Response, unknown, ResolvedRequestOptions>
Config, }
MethodFn,
BuildUrlFn,
SseFn
> & {
interceptors: Middleware<Request, Response, unknown, ResolvedRequestOptions>;
};
/** /**
* The `createClientConfig()` function will be called on client initialization * The `createClientConfig()` function will be called on client initialization
@ -217,25 +181,22 @@ export type Client = CoreClient<
*/ */
export type CreateClientConfig<T extends ClientOptions = ClientOptions> = ( export type CreateClientConfig<T extends ClientOptions = ClientOptions> = (
override?: Config<ClientOptions & T>, override?: Config<ClientOptions & T>,
) => Config<Required<ClientOptions> & T>; ) => Config<Required<ClientOptions> & T>
export interface TDataShape { export interface TDataShape {
body?: unknown; body?: unknown
headers?: unknown; headers?: unknown
path?: unknown; path?: unknown
query?: unknown; query?: unknown
url: string; url: string
} }
type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>; type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>
export type Options< export type Options<
TData extends TDataShape = TDataShape, TData extends TDataShape = TDataShape,
ThrowOnError extends boolean = boolean, ThrowOnError extends boolean = boolean,
TResponse = unknown, TResponse = unknown,
TResponseStyle extends ResponseStyle = 'fields', TResponseStyle extends ResponseStyle = "fields",
> = OmitKeys< > = OmitKeys<RequestOptions<TResponse, TResponseStyle, ThrowOnError>, "body" | "path" | "query" | "url"> &
RequestOptions<TResponse, TResponseStyle, ThrowOnError>, ([TData] extends [never] ? unknown : Omit<TData, "url">)
'body' | 'path' | 'query' | 'url'
> &
([TData] extends [never] ? unknown : Omit<TData, 'url'>);

View file

@ -1,332 +1,289 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import { getAuthToken } from '../core/auth.gen.js'; import { getAuthToken } from "../core/auth.gen.js"
import type { QuerySerializerOptions } from '../core/bodySerializer.gen.js'; import type { QuerySerializerOptions } from "../core/bodySerializer.gen.js"
import { jsonBodySerializer } from '../core/bodySerializer.gen.js'; import { jsonBodySerializer } from "../core/bodySerializer.gen.js"
import { import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from "../core/pathSerializer.gen.js"
serializeArrayParam, import { getUrl } from "../core/utils.gen.js"
serializeObjectParam, import type { Client, ClientOptions, Config, RequestOptions } from "./types.gen.js"
serializePrimitiveParam,
} from '../core/pathSerializer.gen.js';
import { getUrl } from '../core/utils.gen.js';
import type { Client, ClientOptions, Config, RequestOptions } from './types.gen.js';
export const createQuerySerializer = <T = unknown>({ export const createQuerySerializer = <T = unknown>({ parameters = {}, ...args }: QuerySerializerOptions = {}) => {
parameters = {},
...args
}: QuerySerializerOptions = {}) => {
const querySerializer = (queryParams: T) => { const querySerializer = (queryParams: T) => {
const search: string[] = []; const search: string[] = []
if (queryParams && typeof queryParams === 'object') { if (queryParams && typeof queryParams === "object") {
for (const name in queryParams) { for (const name in queryParams) {
const value = queryParams[name]; const value = queryParams[name]
if (value === undefined || value === null) { if (value === undefined || value === null) {
continue; continue
} }
const options = parameters[name] || args; const options = parameters[name] || args
if (Array.isArray(value)) { if (Array.isArray(value)) {
const serializedArray = serializeArrayParam({ const serializedArray = serializeArrayParam({
allowReserved: options.allowReserved, allowReserved: options.allowReserved,
explode: true, explode: true,
name, name,
style: 'form', style: "form",
value, value,
...options.array, ...options.array,
}); })
if (serializedArray) search.push(serializedArray); if (serializedArray) search.push(serializedArray)
} else if (typeof value === 'object') { } else if (typeof value === "object") {
const serializedObject = serializeObjectParam({ const serializedObject = serializeObjectParam({
allowReserved: options.allowReserved, allowReserved: options.allowReserved,
explode: true, explode: true,
name, name,
style: 'deepObject', style: "deepObject",
value: value as Record<string, unknown>, value: value as Record<string, unknown>,
...options.object, ...options.object,
}); })
if (serializedObject) search.push(serializedObject); if (serializedObject) search.push(serializedObject)
} else { } else {
const serializedPrimitive = serializePrimitiveParam({ const serializedPrimitive = serializePrimitiveParam({
allowReserved: options.allowReserved, allowReserved: options.allowReserved,
name, name,
value: value as string, value: value as string,
}); })
if (serializedPrimitive) search.push(serializedPrimitive); if (serializedPrimitive) search.push(serializedPrimitive)
} }
} }
} }
return search.join('&'); return search.join("&")
}; }
return querySerializer; return querySerializer
}; }
/** /**
* Infers parseAs value from provided Content-Type header. * Infers parseAs value from provided Content-Type header.
*/ */
export const getParseAs = ( export const getParseAs = (contentType: string | null): Exclude<Config["parseAs"], "auto"> => {
contentType: string | null,
): Exclude<Config['parseAs'], 'auto'> => {
if (!contentType) { if (!contentType) {
// If no Content-Type header is provided, the best we can do is return the raw response body, // If no Content-Type header is provided, the best we can do is return the raw response body,
// which is effectively the same as the 'stream' option. // which is effectively the same as the 'stream' option.
return 'stream'; return "stream"
} }
const cleanContent = contentType.split(';')[0]?.trim(); const cleanContent = contentType.split(";")[0]?.trim()
if (!cleanContent) { if (!cleanContent) {
return; return
} }
if ( if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
cleanContent.startsWith('application/json') || return "json"
cleanContent.endsWith('+json')
) {
return 'json';
} }
if (cleanContent === 'multipart/form-data') { if (cleanContent === "multipart/form-data") {
return 'formData'; return "formData"
} }
if ( if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) {
['application/', 'audio/', 'image/', 'video/'].some((type) => return "blob"
cleanContent.startsWith(type),
)
) {
return 'blob';
} }
if (cleanContent.startsWith('text/')) { if (cleanContent.startsWith("text/")) {
return 'text'; return "text"
} }
return; return
}; }
const checkForExistence = ( const checkForExistence = (
options: Pick<RequestOptions, 'auth' | 'query'> & { options: Pick<RequestOptions, "auth" | "query"> & {
headers: Headers; headers: Headers
}, },
name?: string, name?: string,
): boolean => { ): boolean => {
if (!name) { if (!name) {
return false; return false
} }
if ( if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
options.headers.has(name) || return true
options.query?.[name] ||
options.headers.get('Cookie')?.includes(`${name}=`)
) {
return true;
} }
return false; return false
}; }
export const setAuthParams = async ({ export const setAuthParams = async ({
security, security,
...options ...options
}: Pick<Required<RequestOptions>, 'security'> & }: Pick<Required<RequestOptions>, "security"> &
Pick<RequestOptions, 'auth' | 'query'> & { Pick<RequestOptions, "auth" | "query"> & {
headers: Headers; headers: Headers
}) => { }) => {
for (const auth of security) { for (const auth of security) {
if (checkForExistence(options, auth.name)) { if (checkForExistence(options, auth.name)) {
continue; continue
} }
const token = await getAuthToken(auth, options.auth); const token = await getAuthToken(auth, options.auth)
if (!token) { if (!token) {
continue; continue
} }
const name = auth.name ?? 'Authorization'; const name = auth.name ?? "Authorization"
switch (auth.in) { switch (auth.in) {
case 'query': case "query":
if (!options.query) { if (!options.query) {
options.query = {}; options.query = {}
} }
options.query[name] = token; options.query[name] = token
break; break
case 'cookie': case "cookie":
options.headers.append('Cookie', `${name}=${token}`); options.headers.append("Cookie", `${name}=${token}`)
break; break
case 'header': case "header":
default: default:
options.headers.set(name, token); options.headers.set(name, token)
break; break
} }
} }
}; }
export const buildUrl: Client['buildUrl'] = (options) => export const buildUrl: Client["buildUrl"] = (options) =>
getUrl({ getUrl({
baseUrl: options.baseUrl as string, baseUrl: options.baseUrl as string,
path: options.path, path: options.path,
query: options.query, query: options.query,
querySerializer: querySerializer:
typeof options.querySerializer === 'function' typeof options.querySerializer === "function"
? options.querySerializer ? options.querySerializer
: createQuerySerializer(options.querySerializer), : createQuerySerializer(options.querySerializer),
url: options.url, url: options.url,
}); })
export const mergeConfigs = (a: Config, b: Config): Config => { export const mergeConfigs = (a: Config, b: Config): Config => {
const config = { ...a, ...b }; const config = { ...a, ...b }
if (config.baseUrl?.endsWith('/')) { if (config.baseUrl?.endsWith("/")) {
config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1)
} }
config.headers = mergeHeaders(a.headers, b.headers); config.headers = mergeHeaders(a.headers, b.headers)
return config; return config
}; }
const headersEntries = (headers: Headers): Array<[string, string]> => { const headersEntries = (headers: Headers): Array<[string, string]> => {
const entries: Array<[string, string]> = []; const entries: Array<[string, string]> = []
headers.forEach((value, key) => { headers.forEach((value, key) => {
entries.push([key, value]); entries.push([key, value])
}); })
return entries; return entries
}; }
export const mergeHeaders = ( export const mergeHeaders = (...headers: Array<Required<Config>["headers"] | undefined>): Headers => {
...headers: Array<Required<Config>['headers'] | undefined> const mergedHeaders = new Headers()
): Headers => {
const mergedHeaders = new Headers();
for (const header of headers) { for (const header of headers) {
if (!header) { if (!header) {
continue; continue
} }
const iterator = const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header)
header instanceof Headers
? headersEntries(header)
: Object.entries(header);
for (const [key, value] of iterator) { for (const [key, value] of iterator) {
if (value === null) { if (value === null) {
mergedHeaders.delete(key); mergedHeaders.delete(key)
} else if (Array.isArray(value)) { } else if (Array.isArray(value)) {
for (const v of value) { for (const v of value) {
mergedHeaders.append(key, v as string); mergedHeaders.append(key, v as string)
} }
} else if (value !== undefined) { } else if (value !== undefined) {
// assume object headers are meant to be JSON stringified, i.e. their // assume object headers are meant to be JSON stringified, i.e. their
// content value in OpenAPI specification is 'application/json' // content value in OpenAPI specification is 'application/json'
mergedHeaders.set( mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : (value as string))
key,
typeof value === 'object' ? JSON.stringify(value) : (value as string),
);
} }
} }
} }
return mergedHeaders; return mergedHeaders
}; }
type ErrInterceptor<Err, Res, Req, Options> = ( type ErrInterceptor<Err, Res, Req, Options> = (
error: Err, error: Err,
response: Res, response: Res,
request: Req, request: Req,
options: Options, options: Options,
) => Err | Promise<Err>; ) => Err | Promise<Err>
type ReqInterceptor<Req, Options> = ( type ReqInterceptor<Req, Options> = (request: Req, options: Options) => Req | Promise<Req>
request: Req,
options: Options,
) => Req | Promise<Req>;
type ResInterceptor<Res, Req, Options> = ( type ResInterceptor<Res, Req, Options> = (response: Res, request: Req, options: Options) => Res | Promise<Res>
response: Res,
request: Req,
options: Options,
) => Res | Promise<Res>;
class Interceptors<Interceptor> { class Interceptors<Interceptor> {
fns: Array<Interceptor | null> = []; fns: Array<Interceptor | null> = []
clear(): void { clear(): void {
this.fns = []; this.fns = []
} }
eject(id: number | Interceptor): void { eject(id: number | Interceptor): void {
const index = this.getInterceptorIndex(id); const index = this.getInterceptorIndex(id)
if (this.fns[index]) { if (this.fns[index]) {
this.fns[index] = null; this.fns[index] = null
} }
} }
exists(id: number | Interceptor): boolean { exists(id: number | Interceptor): boolean {
const index = this.getInterceptorIndex(id); const index = this.getInterceptorIndex(id)
return Boolean(this.fns[index]); return Boolean(this.fns[index])
} }
getInterceptorIndex(id: number | Interceptor): number { getInterceptorIndex(id: number | Interceptor): number {
if (typeof id === 'number') { if (typeof id === "number") {
return this.fns[id] ? id : -1; return this.fns[id] ? id : -1
} }
return this.fns.indexOf(id); return this.fns.indexOf(id)
} }
update( update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false {
id: number | Interceptor, const index = this.getInterceptorIndex(id)
fn: Interceptor,
): number | Interceptor | false {
const index = this.getInterceptorIndex(id);
if (this.fns[index]) { if (this.fns[index]) {
this.fns[index] = fn; this.fns[index] = fn
return id; return id
} }
return false; return false
} }
use(fn: Interceptor): number { use(fn: Interceptor): number {
this.fns.push(fn); this.fns.push(fn)
return this.fns.length - 1; return this.fns.length - 1
} }
} }
export interface Middleware<Req, Res, Err, Options> { export interface Middleware<Req, Res, Err, Options> {
error: Interceptors<ErrInterceptor<Err, Res, Req, Options>>; error: Interceptors<ErrInterceptor<Err, Res, Req, Options>>
request: Interceptors<ReqInterceptor<Req, Options>>; request: Interceptors<ReqInterceptor<Req, Options>>
response: Interceptors<ResInterceptor<Res, Req, Options>>; response: Interceptors<ResInterceptor<Res, Req, Options>>
} }
export const createInterceptors = <Req, Res, Err, Options>(): Middleware< export const createInterceptors = <Req, Res, Err, Options>(): Middleware<Req, Res, Err, Options> => ({
Req,
Res,
Err,
Options
> => ({
error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(), error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(),
request: new Interceptors<ReqInterceptor<Req, Options>>(), request: new Interceptors<ReqInterceptor<Req, Options>>(),
response: new Interceptors<ResInterceptor<Res, Req, Options>>(), response: new Interceptors<ResInterceptor<Res, Req, Options>>(),
}); })
const defaultQuerySerializer = createQuerySerializer({ const defaultQuerySerializer = createQuerySerializer({
allowReserved: false, allowReserved: false,
array: { array: {
explode: true, explode: true,
style: 'form', style: "form",
}, },
object: { object: {
explode: true, explode: true,
style: 'deepObject', style: "deepObject",
}, },
}); })
const defaultHeaders = { const defaultHeaders = {
'Content-Type': 'application/json', "Content-Type": "application/json",
}; }
export const createConfig = <T extends ClientOptions = ClientOptions>( export const createConfig = <T extends ClientOptions = ClientOptions>(
override: Config<Omit<ClientOptions, keyof T> & T> = {}, override: Config<Omit<ClientOptions, keyof T> & T> = {},
): Config<Omit<ClientOptions, keyof T> & T> => ({ ): Config<Omit<ClientOptions, keyof T> & T> => ({
...jsonBodySerializer, ...jsonBodySerializer,
headers: defaultHeaders, headers: defaultHeaders,
parseAs: 'auto', parseAs: "auto",
querySerializer: defaultQuerySerializer, querySerializer: defaultQuerySerializer,
...override, ...override,
}); })

View file

@ -1,6 +1,6 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
export type AuthToken = string | undefined; export type AuthToken = string | undefined
export interface Auth { export interface Auth {
/** /**
@ -8,35 +8,34 @@ export interface Auth {
* *
* @default 'header' * @default 'header'
*/ */
in?: 'header' | 'query' | 'cookie'; in?: "header" | "query" | "cookie"
/** /**
* Header or query parameter name. * Header or query parameter name.
* *
* @default 'Authorization' * @default 'Authorization'
*/ */
name?: string; name?: string
scheme?: 'basic' | 'bearer'; scheme?: "basic" | "bearer"
type: 'apiKey' | 'http'; type: "apiKey" | "http"
} }
export const getAuthToken = async ( export const getAuthToken = async (
auth: Auth, auth: Auth,
callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken, callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken,
): Promise<string | undefined> => { ): Promise<string | undefined> => {
const token = const token = typeof callback === "function" ? await callback(auth) : callback
typeof callback === 'function' ? await callback(auth) : callback;
if (!token) { if (!token) {
return; return
} }
if (auth.scheme === 'bearer') { if (auth.scheme === "bearer") {
return `Bearer ${token}`; return `Bearer ${token}`
} }
if (auth.scheme === 'basic') { if (auth.scheme === "basic") {
return `Basic ${btoa(token)}`; return `Basic ${btoa(token)}`
} }
return token; return token
}; }

View file

@ -1,100 +1,82 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import type { import type { ArrayStyle, ObjectStyle, SerializerOptions } from "./pathSerializer.gen.js"
ArrayStyle,
ObjectStyle,
SerializerOptions,
} from './pathSerializer.gen.js';
export type QuerySerializer = (query: Record<string, unknown>) => string; export type QuerySerializer = (query: Record<string, unknown>) => string
export type BodySerializer = (body: any) => any; export type BodySerializer = (body: any) => any
type QuerySerializerOptionsObject = { type QuerySerializerOptionsObject = {
allowReserved?: boolean; allowReserved?: boolean
array?: Partial<SerializerOptions<ArrayStyle>>; array?: Partial<SerializerOptions<ArrayStyle>>
object?: Partial<SerializerOptions<ObjectStyle>>; object?: Partial<SerializerOptions<ObjectStyle>>
}; }
export type QuerySerializerOptions = QuerySerializerOptionsObject & { export type QuerySerializerOptions = QuerySerializerOptionsObject & {
/** /**
* Per-parameter serialization overrides. When provided, these settings * Per-parameter serialization overrides. When provided, these settings
* override the global array/object settings for specific parameter names. * override the global array/object settings for specific parameter names.
*/ */
parameters?: Record<string, QuerySerializerOptionsObject>; parameters?: Record<string, QuerySerializerOptionsObject>
}; }
const serializeFormDataPair = ( const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => {
data: FormData, if (typeof value === "string" || value instanceof Blob) {
key: string, data.append(key, value)
value: unknown,
): void => {
if (typeof value === 'string' || value instanceof Blob) {
data.append(key, value);
} else if (value instanceof Date) { } else if (value instanceof Date) {
data.append(key, value.toISOString()); data.append(key, value.toISOString())
} else { } else {
data.append(key, JSON.stringify(value)); data.append(key, JSON.stringify(value))
} }
}; }
const serializeUrlSearchParamsPair = ( const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: unknown): void => {
data: URLSearchParams, if (typeof value === "string") {
key: string, data.append(key, value)
value: unknown,
): void => {
if (typeof value === 'string') {
data.append(key, value);
} else { } else {
data.append(key, JSON.stringify(value)); data.append(key, JSON.stringify(value))
} }
}; }
export const formDataBodySerializer = { export const formDataBodySerializer = {
bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>( bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): FormData => {
body: T, const data = new FormData()
): FormData => {
const data = new FormData();
Object.entries(body).forEach(([key, value]) => { Object.entries(body).forEach(([key, value]) => {
if (value === undefined || value === null) { if (value === undefined || value === null) {
return; return
} }
if (Array.isArray(value)) { if (Array.isArray(value)) {
value.forEach((v) => serializeFormDataPair(data, key, v)); value.forEach((v) => serializeFormDataPair(data, key, v))
} else { } else {
serializeFormDataPair(data, key, value); serializeFormDataPair(data, key, value)
} }
}); })
return data; return data
}, },
}; }
export const jsonBodySerializer = { export const jsonBodySerializer = {
bodySerializer: <T>(body: T): string => bodySerializer: <T>(body: T): string =>
JSON.stringify(body, (_key, value) => JSON.stringify(body, (_key, value) => (typeof value === "bigint" ? value.toString() : value)),
typeof value === 'bigint' ? value.toString() : value, }
),
};
export const urlSearchParamsBodySerializer = { export const urlSearchParamsBodySerializer = {
bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>( bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): string => {
body: T, const data = new URLSearchParams()
): string => {
const data = new URLSearchParams();
Object.entries(body).forEach(([key, value]) => { Object.entries(body).forEach(([key, value]) => {
if (value === undefined || value === null) { if (value === undefined || value === null) {
return; return
} }
if (Array.isArray(value)) { if (Array.isArray(value)) {
value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); value.forEach((v) => serializeUrlSearchParamsPair(data, key, v))
} else { } else {
serializeUrlSearchParamsPair(data, key, value); serializeUrlSearchParamsPair(data, key, value)
} }
}); })
return data.toString(); return data.toString()
}, },
}; }

View file

@ -1,167 +1,160 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
type Slot = 'body' | 'headers' | 'path' | 'query'; type Slot = "body" | "headers" | "path" | "query"
export type Field = export type Field =
| { | {
in: Exclude<Slot, 'body'>; in: Exclude<Slot, "body">
/** /**
* Field name. This is the name we want the user to see and use. * Field name. This is the name we want the user to see and use.
*/ */
key: string; key: string
/** /**
* Field mapped name. This is the name we want to use in the request. * Field mapped name. This is the name we want to use in the request.
* If omitted, we use the same value as `key`. * If omitted, we use the same value as `key`.
*/ */
map?: string; map?: string
} }
| { | {
in: Extract<Slot, 'body'>; in: Extract<Slot, "body">
/** /**
* Key isn't required for bodies. * Key isn't required for bodies.
*/ */
key?: string; key?: string
map?: string; map?: string
} }
| { | {
/** /**
* Field name. This is the name we want the user to see and use. * Field name. This is the name we want the user to see and use.
*/ */
key: string; key: string
/** /**
* Field mapped name. This is the name we want to use in the request. * Field mapped name. This is the name we want to use in the request.
* If `in` is omitted, `map` aliases `key` to the transport layer. * If `in` is omitted, `map` aliases `key` to the transport layer.
*/ */
map: Slot; map: Slot
}; }
export interface Fields { export interface Fields {
allowExtra?: Partial<Record<Slot, boolean>>; allowExtra?: Partial<Record<Slot, boolean>>
args?: ReadonlyArray<Field>; args?: ReadonlyArray<Field>
} }
export type FieldsConfig = ReadonlyArray<Field | Fields>; export type FieldsConfig = ReadonlyArray<Field | Fields>
const extraPrefixesMap: Record<string, Slot> = { const extraPrefixesMap: Record<string, Slot> = {
$body_: 'body', $body_: "body",
$headers_: 'headers', $headers_: "headers",
$path_: 'path', $path_: "path",
$query_: 'query', $query_: "query",
}; }
const extraPrefixes = Object.entries(extraPrefixesMap); const extraPrefixes = Object.entries(extraPrefixesMap)
type KeyMap = Map< type KeyMap = Map<
string, string,
| { | {
in: Slot; in: Slot
map?: string; map?: string
} }
| { | {
in?: never; in?: never
map: Slot; map: Slot
} }
>; >
const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
if (!map) { if (!map) {
map = new Map(); map = new Map()
} }
for (const config of fields) { for (const config of fields) {
if ('in' in config) { if ("in" in config) {
if (config.key) { if (config.key) {
map.set(config.key, { map.set(config.key, {
in: config.in, in: config.in,
map: config.map, map: config.map,
}); })
} }
} else if ('key' in config) { } else if ("key" in config) {
map.set(config.key, { map.set(config.key, {
map: config.map, map: config.map,
}); })
} else if (config.args) { } else if (config.args) {
buildKeyMap(config.args, map); buildKeyMap(config.args, map)
} }
} }
return map; return map
}; }
interface Params { interface Params {
body: unknown; body: unknown
headers: Record<string, unknown>; headers: Record<string, unknown>
path: Record<string, unknown>; path: Record<string, unknown>
query: Record<string, unknown>; query: Record<string, unknown>
} }
const stripEmptySlots = (params: Params) => { const stripEmptySlots = (params: Params) => {
for (const [slot, value] of Object.entries(params)) { for (const [slot, value] of Object.entries(params)) {
if (value && typeof value === 'object' && !Object.keys(value).length) { if (value && typeof value === "object" && !Object.keys(value).length) {
delete params[slot as Slot]; delete params[slot as Slot]
} }
} }
}; }
export const buildClientParams = ( export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsConfig) => {
args: ReadonlyArray<unknown>,
fields: FieldsConfig,
) => {
const params: Params = { const params: Params = {
body: {}, body: {},
headers: {}, headers: {},
path: {}, path: {},
query: {}, query: {},
}; }
const map = buildKeyMap(fields); const map = buildKeyMap(fields)
let config: FieldsConfig[number] | undefined; let config: FieldsConfig[number] | undefined
for (const [index, arg] of args.entries()) { for (const [index, arg] of args.entries()) {
if (fields[index]) { if (fields[index]) {
config = fields[index]; config = fields[index]
} }
if (!config) { if (!config) {
continue; continue
} }
if ('in' in config) { if ("in" in config) {
if (config.key) { if (config.key) {
const field = map.get(config.key)!; const field = map.get(config.key)!
const name = field.map || config.key; const name = field.map || config.key
if (field.in) { if (field.in) {
(params[field.in] as Record<string, unknown>)[name] = arg; ;(params[field.in] as Record<string, unknown>)[name] = arg
} }
} else { } else {
params.body = arg; params.body = arg
} }
} else { } else {
for (const [key, value] of Object.entries(arg ?? {})) { for (const [key, value] of Object.entries(arg ?? {})) {
const field = map.get(key); const field = map.get(key)
if (field) { if (field) {
if (field.in) { if (field.in) {
const name = field.map || key; const name = field.map || key
(params[field.in] as Record<string, unknown>)[name] = value; ;(params[field.in] as Record<string, unknown>)[name] = value
} else { } else {
params[field.map] = value; params[field.map] = value
} }
} else { } else {
const extra = extraPrefixes.find(([prefix]) => const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix))
key.startsWith(prefix),
);
if (extra) { if (extra) {
const [prefix, slot] = extra; const [prefix, slot] = extra
(params[slot] as Record<string, unknown>)[ ;(params[slot] as Record<string, unknown>)[key.slice(prefix.length)] = value
key.slice(prefix.length) } else if ("allowExtra" in config && config.allowExtra) {
] = value;
} else if ('allowExtra' in config && config.allowExtra) {
for (const [slot, allowed] of Object.entries(config.allowExtra)) { for (const [slot, allowed] of Object.entries(config.allowExtra)) {
if (allowed) { if (allowed) {
(params[slot as Slot] as Record<string, unknown>)[key] = value; ;(params[slot as Slot] as Record<string, unknown>)[key] = value
break; break
} }
} }
} }
@ -170,7 +163,7 @@ export const buildClientParams = (
} }
} }
stripEmptySlots(params); stripEmptySlots(params)
return params; return params
}; }

View file

@ -1,70 +1,68 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
interface SerializeOptions<T> interface SerializeOptions<T> extends SerializePrimitiveOptions, SerializerOptions<T> {}
extends SerializePrimitiveOptions,
SerializerOptions<T> {}
interface SerializePrimitiveOptions { interface SerializePrimitiveOptions {
allowReserved?: boolean; allowReserved?: boolean
name: string; name: string
} }
export interface SerializerOptions<T> { export interface SerializerOptions<T> {
/** /**
* @default true * @default true
*/ */
explode: boolean; explode: boolean
style: T; style: T
} }
export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; export type ArrayStyle = "form" | "spaceDelimited" | "pipeDelimited"
export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; export type ArraySeparatorStyle = ArrayStyle | MatrixStyle
type MatrixStyle = 'label' | 'matrix' | 'simple'; type MatrixStyle = "label" | "matrix" | "simple"
export type ObjectStyle = 'form' | 'deepObject'; export type ObjectStyle = "form" | "deepObject"
type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; type ObjectSeparatorStyle = ObjectStyle | MatrixStyle
interface SerializePrimitiveParam extends SerializePrimitiveOptions { interface SerializePrimitiveParam extends SerializePrimitiveOptions {
value: string; value: string
} }
export const separatorArrayExplode = (style: ArraySeparatorStyle) => { export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
switch (style) { switch (style) {
case 'label': case "label":
return '.'; return "."
case 'matrix': case "matrix":
return ';'; return ";"
case 'simple': case "simple":
return ','; return ","
default: default:
return '&'; return "&"
} }
}; }
export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
switch (style) { switch (style) {
case 'form': case "form":
return ','; return ","
case 'pipeDelimited': case "pipeDelimited":
return '|'; return "|"
case 'spaceDelimited': case "spaceDelimited":
return '%20'; return "%20"
default: default:
return ','; return ","
} }
}; }
export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { export const separatorObjectExplode = (style: ObjectSeparatorStyle) => {
switch (style) { switch (style) {
case 'label': case "label":
return '.'; return "."
case 'matrix': case "matrix":
return ';'; return ";"
case 'simple': case "simple":
return ','; return ","
default: default:
return '&'; return "&"
} }
}; }
export const serializeArrayParam = ({ export const serializeArrayParam = ({
allowReserved, allowReserved,
@ -73,60 +71,54 @@ export const serializeArrayParam = ({
style, style,
value, value,
}: SerializeOptions<ArraySeparatorStyle> & { }: SerializeOptions<ArraySeparatorStyle> & {
value: unknown[]; value: unknown[]
}) => { }) => {
if (!explode) { if (!explode) {
const joinedValues = ( const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v as string))).join(
allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) separatorArrayNoExplode(style),
).join(separatorArrayNoExplode(style)); )
switch (style) { switch (style) {
case 'label': case "label":
return `.${joinedValues}`; return `.${joinedValues}`
case 'matrix': case "matrix":
return `;${name}=${joinedValues}`; return `;${name}=${joinedValues}`
case 'simple': case "simple":
return joinedValues; return joinedValues
default: default:
return `${name}=${joinedValues}`; return `${name}=${joinedValues}`
} }
} }
const separator = separatorArrayExplode(style); const separator = separatorArrayExplode(style)
const joinedValues = value const joinedValues = value
.map((v) => { .map((v) => {
if (style === 'label' || style === 'simple') { if (style === "label" || style === "simple") {
return allowReserved ? v : encodeURIComponent(v as string); return allowReserved ? v : encodeURIComponent(v as string)
} }
return serializePrimitiveParam({ return serializePrimitiveParam({
allowReserved, allowReserved,
name, name,
value: v as string, value: v as string,
}); })
}) })
.join(separator); .join(separator)
return style === 'label' || style === 'matrix' return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues
? separator + joinedValues }
: joinedValues;
};
export const serializePrimitiveParam = ({ export const serializePrimitiveParam = ({ allowReserved, name, value }: SerializePrimitiveParam) => {
allowReserved,
name,
value,
}: SerializePrimitiveParam) => {
if (value === undefined || value === null) { if (value === undefined || value === null) {
return ''; return ""
} }
if (typeof value === 'object') { if (typeof value === "object") {
throw new Error( throw new Error(
'Deeply-nested arrays/objects arent supported. Provide your own `querySerializer()` to handle these.', "Deeply-nested arrays/objects arent supported. Provide your own `querySerializer()` to handle these.",
); )
} }
return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; return `${name}=${allowReserved ? value : encodeURIComponent(value)}`
}; }
export const serializeObjectParam = ({ export const serializeObjectParam = ({
allowReserved, allowReserved,
@ -136,46 +128,40 @@ export const serializeObjectParam = ({
value, value,
valueOnly, valueOnly,
}: SerializeOptions<ObjectSeparatorStyle> & { }: SerializeOptions<ObjectSeparatorStyle> & {
value: Record<string, unknown> | Date; value: Record<string, unknown> | Date
valueOnly?: boolean; valueOnly?: boolean
}) => { }) => {
if (value instanceof Date) { if (value instanceof Date) {
return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`
} }
if (style !== 'deepObject' && !explode) { if (style !== "deepObject" && !explode) {
let values: string[] = []; let values: string[] = []
Object.entries(value).forEach(([key, v]) => { Object.entries(value).forEach(([key, v]) => {
values = [ values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)]
...values, })
key, const joinedValues = values.join(",")
allowReserved ? (v as string) : encodeURIComponent(v as string),
];
});
const joinedValues = values.join(',');
switch (style) { switch (style) {
case 'form': case "form":
return `${name}=${joinedValues}`; return `${name}=${joinedValues}`
case 'label': case "label":
return `.${joinedValues}`; return `.${joinedValues}`
case 'matrix': case "matrix":
return `;${name}=${joinedValues}`; return `;${name}=${joinedValues}`
default: default:
return joinedValues; return joinedValues
} }
} }
const separator = separatorObjectExplode(style); const separator = separatorObjectExplode(style)
const joinedValues = Object.entries(value) const joinedValues = Object.entries(value)
.map(([key, v]) => .map(([key, v]) =>
serializePrimitiveParam({ serializePrimitiveParam({
allowReserved, allowReserved,
name: style === 'deepObject' ? `${name}[${key}]` : key, name: style === "deepObject" ? `${name}[${key}]` : key,
value: v as string, value: v as string,
}), }),
) )
.join(separator); .join(separator)
return style === 'label' || style === 'matrix' return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues
? separator + joinedValues }
: joinedValues;
};

View file

@ -3,134 +3,109 @@
/** /**
* JSON-friendly union that mirrors what Pinia Colada can hash. * JSON-friendly union that mirrors what Pinia Colada can hash.
*/ */
export type JsonValue = export type JsonValue = null | string | number | boolean | JsonValue[] | { [key: string]: JsonValue }
| null
| string
| number
| boolean
| JsonValue[]
| { [key: string]: JsonValue };
/** /**
* Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes.
*/ */
export const queryKeyJsonReplacer = (_key: string, value: unknown) => { export const queryKeyJsonReplacer = (_key: string, value: unknown) => {
if ( if (value === undefined || typeof value === "function" || typeof value === "symbol") {
value === undefined || return undefined
typeof value === 'function' ||
typeof value === 'symbol'
) {
return undefined;
} }
if (typeof value === 'bigint') { if (typeof value === "bigint") {
return value.toString(); return value.toString()
} }
if (value instanceof Date) { if (value instanceof Date) {
return value.toISOString(); return value.toISOString()
} }
return value; return value
}; }
/** /**
* Safely stringifies a value and parses it back into a JsonValue. * Safely stringifies a value and parses it back into a JsonValue.
*/ */
export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => {
try { try {
const json = JSON.stringify(input, queryKeyJsonReplacer); const json = JSON.stringify(input, queryKeyJsonReplacer)
if (json === undefined) { if (json === undefined) {
return undefined; return undefined
} }
return JSON.parse(json) as JsonValue; return JSON.parse(json) as JsonValue
} catch { } catch {
return undefined; return undefined
} }
}; }
/** /**
* Detects plain objects (including objects with a null prototype). * Detects plain objects (including objects with a null prototype).
*/ */
const isPlainObject = (value: unknown): value is Record<string, unknown> => { const isPlainObject = (value: unknown): value is Record<string, unknown> => {
if (value === null || typeof value !== 'object') { if (value === null || typeof value !== "object") {
return false; return false
} }
const prototype = Object.getPrototypeOf(value as object); const prototype = Object.getPrototypeOf(value as object)
return prototype === Object.prototype || prototype === null; return prototype === Object.prototype || prototype === null
}; }
/** /**
* Turns URLSearchParams into a sorted JSON object for deterministic keys. * Turns URLSearchParams into a sorted JSON object for deterministic keys.
*/ */
const serializeSearchParams = (params: URLSearchParams): JsonValue => { const serializeSearchParams = (params: URLSearchParams): JsonValue => {
const entries = Array.from(params.entries()).sort(([a], [b]) => const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b))
a.localeCompare(b), const result: Record<string, JsonValue> = {}
);
const result: Record<string, JsonValue> = {};
for (const [key, value] of entries) { for (const [key, value] of entries) {
const existing = result[key]; const existing = result[key]
if (existing === undefined) { if (existing === undefined) {
result[key] = value; result[key] = value
continue; continue
} }
if (Array.isArray(existing)) { if (Array.isArray(existing)) {
(existing as string[]).push(value); ;(existing as string[]).push(value)
} else { } else {
result[key] = [existing, value]; result[key] = [existing, value]
} }
} }
return result; return result
}; }
/** /**
* Normalizes any accepted value into a JSON-friendly shape for query keys. * Normalizes any accepted value into a JSON-friendly shape for query keys.
*/ */
export const serializeQueryKeyValue = ( export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => {
value: unknown,
): JsonValue | undefined => {
if (value === null) { if (value === null) {
return null; return null
} }
if ( if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
typeof value === 'string' || return value
typeof value === 'number' ||
typeof value === 'boolean'
) {
return value;
} }
if ( if (value === undefined || typeof value === "function" || typeof value === "symbol") {
value === undefined || return undefined
typeof value === 'function' ||
typeof value === 'symbol'
) {
return undefined;
} }
if (typeof value === 'bigint') { if (typeof value === "bigint") {
return value.toString(); return value.toString()
} }
if (value instanceof Date) { if (value instanceof Date) {
return value.toISOString(); return value.toISOString()
} }
if (Array.isArray(value)) { if (Array.isArray(value)) {
return stringifyToJsonValue(value); return stringifyToJsonValue(value)
} }
if ( if (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) {
typeof URLSearchParams !== 'undefined' && return serializeSearchParams(value)
value instanceof URLSearchParams
) {
return serializeSearchParams(value);
} }
if (isPlainObject(value)) { if (isPlainObject(value)) {
return stringifyToJsonValue(value); return stringifyToJsonValue(value)
} }
return undefined; return undefined
}; }

View file

@ -1,23 +1,20 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import type { Config } from './types.gen.js'; import type { Config } from "./types.gen.js"
export type ServerSentEventsOptions<TData = unknown> = Omit< export type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, "method"> &
RequestInit, Pick<Config, "method" | "responseTransformer" | "responseValidator"> & {
'method'
> &
Pick<Config, 'method' | 'responseTransformer' | 'responseValidator'> & {
/** /**
* Fetch API implementation. You can use this option to provide a custom * Fetch API implementation. You can use this option to provide a custom
* fetch instance. * fetch instance.
* *
* @default globalThis.fetch * @default globalThis.fetch
*/ */
fetch?: typeof fetch; fetch?: typeof fetch
/** /**
* Implementing clients can call request interceptors inside this hook. * Implementing clients can call request interceptors inside this hook.
*/ */
onRequest?: (url: string, init: RequestInit) => Promise<Request>; onRequest?: (url: string, init: RequestInit) => Promise<Request>
/** /**
* Callback invoked when a network or parsing error occurs during streaming. * Callback invoked when a network or parsing error occurs during streaming.
* *
@ -25,7 +22,7 @@ export type ServerSentEventsOptions<TData = unknown> = Omit<
* *
* @param error The error that occurred. * @param error The error that occurred.
*/ */
onSseError?: (error: unknown) => void; onSseError?: (error: unknown) => void
/** /**
* Callback invoked when an event is streamed from the server. * Callback invoked when an event is streamed from the server.
* *
@ -34,8 +31,8 @@ export type ServerSentEventsOptions<TData = unknown> = Omit<
* @param event Event streamed from the server. * @param event Event streamed from the server.
* @returns Nothing (void). * @returns Nothing (void).
*/ */
onSseEvent?: (event: StreamEvent<TData>) => void; onSseEvent?: (event: StreamEvent<TData>) => void
serializedBody?: RequestInit['body']; serializedBody?: RequestInit["body"]
/** /**
* Default retry delay in milliseconds. * Default retry delay in milliseconds.
* *
@ -43,11 +40,11 @@ export type ServerSentEventsOptions<TData = unknown> = Omit<
* *
* @default 3000 * @default 3000
*/ */
sseDefaultRetryDelay?: number; sseDefaultRetryDelay?: number
/** /**
* Maximum number of retry attempts before giving up. * Maximum number of retry attempts before giving up.
*/ */
sseMaxRetryAttempts?: number; sseMaxRetryAttempts?: number
/** /**
* Maximum retry delay in milliseconds. * Maximum retry delay in milliseconds.
* *
@ -57,34 +54,26 @@ export type ServerSentEventsOptions<TData = unknown> = Omit<
* *
* @default 30000 * @default 30000
*/ */
sseMaxRetryDelay?: number; sseMaxRetryDelay?: number
/** /**
* Optional sleep function for retry backoff. * Optional sleep function for retry backoff.
* *
* Defaults to using `setTimeout`. * Defaults to using `setTimeout`.
*/ */
sseSleepFn?: (ms: number) => Promise<void>; sseSleepFn?: (ms: number) => Promise<void>
url: string; url: string
}; }
export interface StreamEvent<TData = unknown> { export interface StreamEvent<TData = unknown> {
data: TData; data: TData
event?: string; event?: string
id?: string; id?: string
retry?: number; retry?: number
} }
export type ServerSentEventsResult< export type ServerSentEventsResult<TData = unknown, TReturn = void, TNext = unknown> = {
TData = unknown, stream: AsyncGenerator<TData extends Record<string, unknown> ? TData[keyof TData] : TData, TReturn, TNext>
TReturn = void, }
TNext = unknown,
> = {
stream: AsyncGenerator<
TData extends Record<string, unknown> ? TData[keyof TData] : TData,
TReturn,
TNext
>;
};
export const createSseClient = <TData = unknown>({ export const createSseClient = <TData = unknown>({
onRequest, onRequest,
@ -99,123 +88,113 @@ export const createSseClient = <TData = unknown>({
url, url,
...options ...options
}: ServerSentEventsOptions): ServerSentEventsResult<TData> => { }: ServerSentEventsOptions): ServerSentEventsResult<TData> => {
let lastEventId: string | undefined; let lastEventId: string | undefined
const sleep = const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)))
sseSleepFn ??
((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));
const createStream = async function* () { const createStream = async function* () {
let retryDelay: number = sseDefaultRetryDelay ?? 3000; let retryDelay: number = sseDefaultRetryDelay ?? 3000
let attempt = 0; let attempt = 0
const signal = options.signal ?? new AbortController().signal; const signal = options.signal ?? new AbortController().signal
while (true) { while (true) {
if (signal.aborted) break; if (signal.aborted) break
attempt++; attempt++
const headers = const headers =
options.headers instanceof Headers options.headers instanceof Headers
? options.headers ? options.headers
: new Headers(options.headers as Record<string, string> | undefined); : new Headers(options.headers as Record<string, string> | undefined)
if (lastEventId !== undefined) { if (lastEventId !== undefined) {
headers.set('Last-Event-ID', lastEventId); headers.set("Last-Event-ID", lastEventId)
} }
try { try {
const requestInit: RequestInit = { const requestInit: RequestInit = {
redirect: 'follow', redirect: "follow",
...options, ...options,
body: options.serializedBody, body: options.serializedBody,
headers, headers,
signal, signal,
}; }
let request = new Request(url, requestInit); let request = new Request(url, requestInit)
if (onRequest) { if (onRequest) {
request = await onRequest(url, requestInit); request = await onRequest(url, requestInit)
} }
// fetch must be assigned here, otherwise it would throw the error: // fetch must be assigned here, otherwise it would throw the error:
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
const _fetch = options.fetch ?? globalThis.fetch; const _fetch = options.fetch ?? globalThis.fetch
const response = await _fetch(request); const response = await _fetch(request)
if (!response.ok) if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`)
throw new Error(
`SSE failed: ${response.status} ${response.statusText}`,
);
if (!response.body) throw new Error('No body in SSE response'); if (!response.body) throw new Error("No body in SSE response")
const reader = response.body const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()
.pipeThrough(new TextDecoderStream())
.getReader();
let buffer = ''; let buffer = ""
const abortHandler = () => { const abortHandler = () => {
try { try {
reader.cancel(); reader.cancel()
} catch { } catch {
// noop // noop
} }
}; }
signal.addEventListener('abort', abortHandler); signal.addEventListener("abort", abortHandler)
try { try {
while (true) { while (true) {
const { done, value } = await reader.read(); const { done, value } = await reader.read()
if (done) break; if (done) break
buffer += value; buffer += value
const chunks = buffer.split('\n\n'); const chunks = buffer.split("\n\n")
buffer = chunks.pop() ?? ''; buffer = chunks.pop() ?? ""
for (const chunk of chunks) { for (const chunk of chunks) {
const lines = chunk.split('\n'); const lines = chunk.split("\n")
const dataLines: Array<string> = []; const dataLines: Array<string> = []
let eventName: string | undefined; let eventName: string | undefined
for (const line of lines) { for (const line of lines) {
if (line.startsWith('data:')) { if (line.startsWith("data:")) {
dataLines.push(line.replace(/^data:\s*/, '')); dataLines.push(line.replace(/^data:\s*/, ""))
} else if (line.startsWith('event:')) { } else if (line.startsWith("event:")) {
eventName = line.replace(/^event:\s*/, ''); eventName = line.replace(/^event:\s*/, "")
} else if (line.startsWith('id:')) { } else if (line.startsWith("id:")) {
lastEventId = line.replace(/^id:\s*/, ''); lastEventId = line.replace(/^id:\s*/, "")
} else if (line.startsWith('retry:')) { } else if (line.startsWith("retry:")) {
const parsed = Number.parseInt( const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10)
line.replace(/^retry:\s*/, ''),
10,
);
if (!Number.isNaN(parsed)) { if (!Number.isNaN(parsed)) {
retryDelay = parsed; retryDelay = parsed
} }
} }
} }
let data: unknown; let data: unknown
let parsedJson = false; let parsedJson = false
if (dataLines.length) { if (dataLines.length) {
const rawData = dataLines.join('\n'); const rawData = dataLines.join("\n")
try { try {
data = JSON.parse(rawData); data = JSON.parse(rawData)
parsedJson = true; parsedJson = true
} catch { } catch {
data = rawData; data = rawData
} }
} }
if (parsedJson) { if (parsedJson) {
if (responseValidator) { if (responseValidator) {
await responseValidator(data); await responseValidator(data)
} }
if (responseTransformer) { if (responseTransformer) {
data = await responseTransformer(data); data = await responseTransformer(data)
} }
} }
@ -224,41 +203,35 @@ export const createSseClient = <TData = unknown>({
event: eventName, event: eventName,
id: lastEventId, id: lastEventId,
retry: retryDelay, retry: retryDelay,
}); })
if (dataLines.length) { if (dataLines.length) {
yield data as any; yield data as any
} }
} }
} }
} finally { } finally {
signal.removeEventListener('abort', abortHandler); signal.removeEventListener("abort", abortHandler)
reader.releaseLock(); reader.releaseLock()
} }
break; // exit loop on normal completion break // exit loop on normal completion
} catch (error) { } catch (error) {
// connection failed or aborted; retry after delay // connection failed or aborted; retry after delay
onSseError?.(error); onSseError?.(error)
if ( if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) {
sseMaxRetryAttempts !== undefined && break // stop after firing error
attempt >= sseMaxRetryAttempts
) {
break; // stop after firing error
} }
// exponential backoff: double retry each attempt, cap at 30s // exponential backoff: double retry each attempt, cap at 30s
const backoff = Math.min( const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000)
retryDelay * 2 ** (attempt - 1), await sleep(backoff)
sseMaxRetryDelay ?? 30000,
);
await sleep(backoff);
} }
} }
}; }
const stream = createStream(); const stream = createStream()
return { stream }; return { stream }
}; }

View file

@ -1,54 +1,33 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import type { Auth, AuthToken } from './auth.gen.js'; import type { Auth, AuthToken } from "./auth.gen.js"
import type { import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.gen.js"
BodySerializer,
QuerySerializer,
QuerySerializerOptions,
} from './bodySerializer.gen.js';
export type HttpMethod = export type HttpMethod = "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace"
| 'connect'
| 'delete'
| 'get'
| 'head'
| 'options'
| 'patch'
| 'post'
| 'put'
| 'trace';
export type Client< export type Client<RequestFn = never, Config = unknown, MethodFn = never, BuildUrlFn = never, SseFn = never> = {
RequestFn = never,
Config = unknown,
MethodFn = never,
BuildUrlFn = never,
SseFn = never,
> = {
/** /**
* Returns the final request URL. * Returns the final request URL.
*/ */
buildUrl: BuildUrlFn; buildUrl: BuildUrlFn
getConfig: () => Config; getConfig: () => Config
request: RequestFn; request: RequestFn
setConfig: (config: Config) => Config; setConfig: (config: Config) => Config
} & { } & {
[K in HttpMethod]: MethodFn; [K in HttpMethod]: MethodFn
} & ([SseFn] extends [never] } & ([SseFn] extends [never] ? { sse?: never } : { sse: { [K in HttpMethod]: SseFn } })
? { sse?: never }
: { sse: { [K in HttpMethod]: SseFn } });
export interface Config { export interface Config {
/** /**
* Auth token or a function returning auth token. The resolved value will be * Auth token or a function returning auth token. The resolved value will be
* added to the request payload as defined by its `security` array. * added to the request payload as defined by its `security` array.
*/ */
auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken; auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken
/** /**
* A function for serializing request body parameter. By default, * A function for serializing request body parameter. By default,
* {@link JSON.stringify()} will be used. * {@link JSON.stringify()} will be used.
*/ */
bodySerializer?: BodySerializer | null; bodySerializer?: BodySerializer | null
/** /**
* An object containing any HTTP headers that you want to pre-populate your * An object containing any HTTP headers that you want to pre-populate your
* `Headers` object with. * `Headers` object with.
@ -56,23 +35,14 @@ export interface Config {
* {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
*/ */
headers?: headers?:
| RequestInit['headers'] | RequestInit["headers"]
| Record< | Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined | unknown>
string,
| string
| number
| boolean
| (string | number | boolean)[]
| null
| undefined
| unknown
>;
/** /**
* The request method. * The request method.
* *
* {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
*/ */
method?: Uppercase<HttpMethod>; method?: Uppercase<HttpMethod>
/** /**
* A function for serializing request query parameters. By default, arrays * A function for serializing request query parameters. By default, arrays
* will be exploded in form style, objects will be exploded in deepObject * will be exploded in form style, objects will be exploded in deepObject
@ -83,24 +53,24 @@ export interface Config {
* *
* {@link https://swagger.io/docs/specification/serialization/#query View examples} * {@link https://swagger.io/docs/specification/serialization/#query View examples}
*/ */
querySerializer?: QuerySerializer | QuerySerializerOptions; querySerializer?: QuerySerializer | QuerySerializerOptions
/** /**
* A function validating request data. This is useful if you want to ensure * A function validating request data. This is useful if you want to ensure
* the request conforms to the desired shape, so it can be safely sent to * the request conforms to the desired shape, so it can be safely sent to
* the server. * the server.
*/ */
requestValidator?: (data: unknown) => Promise<unknown>; requestValidator?: (data: unknown) => Promise<unknown>
/** /**
* A function transforming response data before it's returned. This is useful * A function transforming response data before it's returned. This is useful
* for post-processing data, e.g. converting ISO strings into Date objects. * for post-processing data, e.g. converting ISO strings into Date objects.
*/ */
responseTransformer?: (data: unknown) => Promise<unknown>; responseTransformer?: (data: unknown) => Promise<unknown>
/** /**
* A function validating response data. This is useful if you want to ensure * A function validating response data. This is useful if you want to ensure
* the response conforms to the desired shape, so it can be safely passed to * the response conforms to the desired shape, so it can be safely passed to
* the transformers and returned to the user. * the transformers and returned to the user.
*/ */
responseValidator?: (data: unknown) => Promise<unknown>; responseValidator?: (data: unknown) => Promise<unknown>
} }
type IsExactlyNeverOrNeverUndefined<T> = [T] extends [never] type IsExactlyNeverOrNeverUndefined<T> = [T] extends [never]
@ -109,10 +79,8 @@ type IsExactlyNeverOrNeverUndefined<T> = [T] extends [never]
? [undefined] extends [T] ? [undefined] extends [T]
? false ? false
: true : true
: false; : false
export type OmitNever<T extends Record<string, unknown>> = { export type OmitNever<T extends Record<string, unknown>> = {
[K in keyof T as IsExactlyNeverOrNeverUndefined<T[K]> extends true [K in keyof T as IsExactlyNeverOrNeverUndefined<T[K]> extends true ? never : K]: T[K]
? never }
: K]: T[K];
};

View file

@ -1,57 +1,54 @@
// This file is auto-generated by @hey-api/openapi-ts // This file is auto-generated by @hey-api/openapi-ts
import type { BodySerializer, QuerySerializer } from './bodySerializer.gen.js'; import type { BodySerializer, QuerySerializer } from "./bodySerializer.gen.js"
import { import {
type ArraySeparatorStyle, type ArraySeparatorStyle,
serializeArrayParam, serializeArrayParam,
serializeObjectParam, serializeObjectParam,
serializePrimitiveParam, serializePrimitiveParam,
} from './pathSerializer.gen.js'; } from "./pathSerializer.gen.js"
export interface PathSerializer { export interface PathSerializer {
path: Record<string, unknown>; path: Record<string, unknown>
url: string; url: string
} }
export const PATH_PARAM_RE = /\{[^{}]+\}/g; export const PATH_PARAM_RE = /\{[^{}]+\}/g
export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
let url = _url; let url = _url
const matches = _url.match(PATH_PARAM_RE); const matches = _url.match(PATH_PARAM_RE)
if (matches) { if (matches) {
for (const match of matches) { for (const match of matches) {
let explode = false; let explode = false
let name = match.substring(1, match.length - 1); let name = match.substring(1, match.length - 1)
let style: ArraySeparatorStyle = 'simple'; let style: ArraySeparatorStyle = "simple"
if (name.endsWith('*')) { if (name.endsWith("*")) {
explode = true; explode = true
name = name.substring(0, name.length - 1); name = name.substring(0, name.length - 1)
} }
if (name.startsWith('.')) { if (name.startsWith(".")) {
name = name.substring(1); name = name.substring(1)
style = 'label'; style = "label"
} else if (name.startsWith(';')) { } else if (name.startsWith(";")) {
name = name.substring(1); name = name.substring(1)
style = 'matrix'; style = "matrix"
} }
const value = path[name]; const value = path[name]
if (value === undefined || value === null) { if (value === undefined || value === null) {
continue; continue
} }
if (Array.isArray(value)) { if (Array.isArray(value)) {
url = url.replace( url = url.replace(match, serializeArrayParam({ explode, name, style, value }))
match, continue
serializeArrayParam({ explode, name, style, value }),
);
continue;
} }
if (typeof value === 'object') { if (typeof value === "object") {
url = url.replace( url = url.replace(
match, match,
serializeObjectParam({ serializeObjectParam({
@ -61,29 +58,27 @@ export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
value: value as Record<string, unknown>, value: value as Record<string, unknown>,
valueOnly: true, valueOnly: true,
}), }),
); )
continue; continue
} }
if (style === 'matrix') { if (style === "matrix") {
url = url.replace( url = url.replace(
match, match,
`;${serializePrimitiveParam({ `;${serializePrimitiveParam({
name, name,
value: value as string, value: value as string,
})}`, })}`,
); )
continue; continue
} }
const replaceValue = encodeURIComponent( const replaceValue = encodeURIComponent(style === "label" ? `.${value as string}` : (value as string))
style === 'label' ? `.${value as string}` : (value as string), url = url.replace(match, replaceValue)
);
url = url.replace(match, replaceValue);
} }
} }
return url; return url
}; }
export const getUrl = ({ export const getUrl = ({
baseUrl, baseUrl,
@ -92,52 +87,51 @@ export const getUrl = ({
querySerializer, querySerializer,
url: _url, url: _url,
}: { }: {
baseUrl?: string; baseUrl?: string
path?: Record<string, unknown>; path?: Record<string, unknown>
query?: Record<string, unknown>; query?: Record<string, unknown>
querySerializer: QuerySerializer; querySerializer: QuerySerializer
url: string; url: string
}) => { }) => {
const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; const pathUrl = _url.startsWith("/") ? _url : `/${_url}`
let url = (baseUrl ?? '') + pathUrl; let url = (baseUrl ?? "") + pathUrl
if (path) { if (path) {
url = defaultPathSerializer({ path, url }); url = defaultPathSerializer({ path, url })
} }
let search = query ? querySerializer(query) : ''; let search = query ? querySerializer(query) : ""
if (search.startsWith('?')) { if (search.startsWith("?")) {
search = search.substring(1); search = search.substring(1)
} }
if (search) { if (search) {
url += `?${search}`; url += `?${search}`
} }
return url; return url
}; }
export function getValidRequestBody(options: { export function getValidRequestBody(options: {
body?: unknown; body?: unknown
bodySerializer?: BodySerializer | null; bodySerializer?: BodySerializer | null
serializedBody?: unknown; serializedBody?: unknown
}) { }) {
const hasBody = options.body !== undefined; const hasBody = options.body !== undefined
const isSerializedBody = hasBody && options.bodySerializer; const isSerializedBody = hasBody && options.bodySerializer
if (isSerializedBody) { if (isSerializedBody) {
if ('serializedBody' in options) { if ("serializedBody" in options) {
const hasSerializedBody = const hasSerializedBody = options.serializedBody !== undefined && options.serializedBody !== ""
options.serializedBody !== undefined && options.serializedBody !== '';
return hasSerializedBody ? options.serializedBody : null; return hasSerializedBody ? options.serializedBody : null
} }
// not all clients implement a serializedBody property (i.e. client-axios) // not all clients implement a serializedBody property (i.e. client-axios)
return options.body !== '' ? options.body : null; return options.body !== "" ? options.body : null
} }
// plain/text body // plain/text body
if (hasBody) { if (hasBody) {
return options.body; return options.body
} }
// no body was provided // no body was provided
return undefined; return undefined
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff