summaryrefslogtreecommitdiffhomepage
path: root/packages/sdk/js/src/v2/gen/core
diff options
context:
space:
mode:
authorDax <[email protected]>2025-12-07 19:04:14 -0500
committerGitHub <[email protected]>2025-12-07 19:04:14 -0500
commitea7ec60f51f9fe3c6382f644c328188a43545b7b (patch)
tree5204cbc059fe10b87c54ffd25782ad83710bdcdb /packages/sdk/js/src/v2/gen/core
parent6667856ba5dac3e5dd77c7008cee2d09be894472 (diff)
downloadopencode-ea7ec60f51f9fe3c6382f644c328188a43545b7b.tar.gz
opencode-ea7ec60f51f9fe3c6382f644c328188a43545b7b.zip
v2 SDK (#5216)
Co-authored-by: GitHub Action <[email protected]>
Diffstat (limited to 'packages/sdk/js/src/v2/gen/core')
-rw-r--r--packages/sdk/js/src/v2/gen/core/auth.gen.ts41
-rw-r--r--packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts82
-rw-r--r--packages/sdk/js/src/v2/gen/core/params.gen.ts169
-rw-r--r--packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts167
-rw-r--r--packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts111
-rw-r--r--packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts237
-rw-r--r--packages/sdk/js/src/v2/gen/core/types.gen.ts86
-rw-r--r--packages/sdk/js/src/v2/gen/core/utils.gen.ts137
8 files changed, 1030 insertions, 0 deletions
diff --git a/packages/sdk/js/src/v2/gen/core/auth.gen.ts b/packages/sdk/js/src/v2/gen/core/auth.gen.ts
new file mode 100644
index 000000000..bc7b230f4
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/auth.gen.ts
@@ -0,0 +1,41 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+export type AuthToken = string | undefined
+
+export interface Auth {
+ /**
+ * Which part of the request do we use to send the auth?
+ *
+ * @default 'header'
+ */
+ in?: "header" | "query" | "cookie"
+ /**
+ * Header or query parameter name.
+ *
+ * @default 'Authorization'
+ */
+ name?: string
+ scheme?: "basic" | "bearer"
+ type: "apiKey" | "http"
+}
+
+export const getAuthToken = async (
+ auth: Auth,
+ callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken,
+): Promise<string | undefined> => {
+ const token = typeof callback === "function" ? await callback(auth) : callback
+
+ if (!token) {
+ return
+ }
+
+ if (auth.scheme === "bearer") {
+ return `Bearer ${token}`
+ }
+
+ if (auth.scheme === "basic") {
+ return `Basic ${btoa(token)}`
+ }
+
+ return token
+}
diff --git a/packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts b/packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts
new file mode 100644
index 000000000..9678fb08e
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/bodySerializer.gen.ts
@@ -0,0 +1,82 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { ArrayStyle, ObjectStyle, SerializerOptions } from "./pathSerializer.gen.js"
+
+export type QuerySerializer = (query: Record<string, unknown>) => string
+
+export type BodySerializer = (body: any) => any
+
+type QuerySerializerOptionsObject = {
+ allowReserved?: boolean
+ array?: Partial<SerializerOptions<ArrayStyle>>
+ object?: Partial<SerializerOptions<ObjectStyle>>
+}
+
+export type QuerySerializerOptions = QuerySerializerOptionsObject & {
+ /**
+ * Per-parameter serialization overrides. When provided, these settings
+ * override the global array/object settings for specific parameter names.
+ */
+ parameters?: Record<string, QuerySerializerOptionsObject>
+}
+
+const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => {
+ if (typeof value === "string" || value instanceof Blob) {
+ data.append(key, value)
+ } else if (value instanceof Date) {
+ data.append(key, value.toISOString())
+ } else {
+ data.append(key, JSON.stringify(value))
+ }
+}
+
+const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: unknown): void => {
+ if (typeof value === "string") {
+ data.append(key, value)
+ } else {
+ data.append(key, JSON.stringify(value))
+ }
+}
+
+export const formDataBodySerializer = {
+ bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): FormData => {
+ const data = new FormData()
+
+ Object.entries(body).forEach(([key, value]) => {
+ if (value === undefined || value === null) {
+ return
+ }
+ if (Array.isArray(value)) {
+ value.forEach((v) => serializeFormDataPair(data, key, v))
+ } else {
+ serializeFormDataPair(data, key, value)
+ }
+ })
+
+ return data
+ },
+}
+
+export const jsonBodySerializer = {
+ bodySerializer: <T>(body: T): string =>
+ JSON.stringify(body, (_key, value) => (typeof value === "bigint" ? value.toString() : value)),
+}
+
+export const urlSearchParamsBodySerializer = {
+ bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): string => {
+ const data = new URLSearchParams()
+
+ Object.entries(body).forEach(([key, value]) => {
+ if (value === undefined || value === null) {
+ return
+ }
+ if (Array.isArray(value)) {
+ value.forEach((v) => serializeUrlSearchParamsPair(data, key, v))
+ } else {
+ serializeUrlSearchParamsPair(data, key, value)
+ }
+ })
+
+ return data.toString()
+ },
+}
diff --git a/packages/sdk/js/src/v2/gen/core/params.gen.ts b/packages/sdk/js/src/v2/gen/core/params.gen.ts
new file mode 100644
index 000000000..6e9d0b9ad
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/params.gen.ts
@@ -0,0 +1,169 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+type Slot = "body" | "headers" | "path" | "query"
+
+export type Field =
+ | {
+ in: Exclude<Slot, "body">
+ /**
+ * Field name. This is the name we want the user to see and use.
+ */
+ key: string
+ /**
+ * Field mapped name. This is the name we want to use in the request.
+ * If omitted, we use the same value as `key`.
+ */
+ map?: string
+ }
+ | {
+ in: Extract<Slot, "body">
+ /**
+ * Key isn't required for bodies.
+ */
+ key?: string
+ map?: string
+ }
+ | {
+ /**
+ * Field name. This is the name we want the user to see and use.
+ */
+ key: string
+ /**
+ * 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.
+ */
+ map: Slot
+ }
+
+export interface Fields {
+ allowExtra?: Partial<Record<Slot, boolean>>
+ args?: ReadonlyArray<Field>
+}
+
+export type FieldsConfig = ReadonlyArray<Field | Fields>
+
+const extraPrefixesMap: Record<string, Slot> = {
+ $body_: "body",
+ $headers_: "headers",
+ $path_: "path",
+ $query_: "query",
+}
+const extraPrefixes = Object.entries(extraPrefixesMap)
+
+type KeyMap = Map<
+ string,
+ | {
+ in: Slot
+ map?: string
+ }
+ | {
+ in?: never
+ map: Slot
+ }
+>
+
+const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
+ if (!map) {
+ map = new Map()
+ }
+
+ for (const config of fields) {
+ if ("in" in config) {
+ if (config.key) {
+ map.set(config.key, {
+ in: config.in,
+ map: config.map,
+ })
+ }
+ } else if ("key" in config) {
+ map.set(config.key, {
+ map: config.map,
+ })
+ } else if (config.args) {
+ buildKeyMap(config.args, map)
+ }
+ }
+
+ return map
+}
+
+interface Params {
+ body: unknown
+ headers: Record<string, unknown>
+ path: Record<string, unknown>
+ query: Record<string, unknown>
+}
+
+const stripEmptySlots = (params: Params) => {
+ for (const [slot, value] of Object.entries(params)) {
+ if (value && typeof value === "object" && !Object.keys(value).length) {
+ delete params[slot as Slot]
+ }
+ }
+}
+
+export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsConfig) => {
+ const params: Params = {
+ body: {},
+ headers: {},
+ path: {},
+ query: {},
+ }
+
+ const map = buildKeyMap(fields)
+
+ let config: FieldsConfig[number] | undefined
+
+ for (const [index, arg] of args.entries()) {
+ if (fields[index]) {
+ config = fields[index]
+ }
+
+ if (!config) {
+ continue
+ }
+
+ if ("in" in config) {
+ if (config.key) {
+ const field = map.get(config.key)!
+ const name = field.map || config.key
+ if (field.in) {
+ ;(params[field.in] as Record<string, unknown>)[name] = arg
+ }
+ } else {
+ params.body = arg
+ }
+ } else {
+ for (const [key, value] of Object.entries(arg ?? {})) {
+ const field = map.get(key)
+
+ if (field) {
+ if (field.in) {
+ const name = field.map || key
+ ;(params[field.in] as Record<string, unknown>)[name] = value
+ } else {
+ params[field.map] = value
+ }
+ } else {
+ const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix))
+
+ if (extra) {
+ const [prefix, slot] = extra
+ ;(params[slot] as Record<string, unknown>)[key.slice(prefix.length)] = value
+ } else if ("allowExtra" in config && config.allowExtra) {
+ for (const [slot, allowed] of Object.entries(config.allowExtra)) {
+ if (allowed) {
+ ;(params[slot as Slot] as Record<string, unknown>)[key] = value
+ break
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ stripEmptySlots(params)
+
+ return params
+}
diff --git a/packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts b/packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts
new file mode 100644
index 000000000..96be3bc5a
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/pathSerializer.gen.ts
@@ -0,0 +1,167 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+interface SerializeOptions<T> extends SerializePrimitiveOptions, SerializerOptions<T> {}
+
+interface SerializePrimitiveOptions {
+ allowReserved?: boolean
+ name: string
+}
+
+export interface SerializerOptions<T> {
+ /**
+ * @default true
+ */
+ explode: boolean
+ style: T
+}
+
+export type ArrayStyle = "form" | "spaceDelimited" | "pipeDelimited"
+export type ArraySeparatorStyle = ArrayStyle | MatrixStyle
+type MatrixStyle = "label" | "matrix" | "simple"
+export type ObjectStyle = "form" | "deepObject"
+type ObjectSeparatorStyle = ObjectStyle | MatrixStyle
+
+interface SerializePrimitiveParam extends SerializePrimitiveOptions {
+ value: string
+}
+
+export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
+ switch (style) {
+ case "label":
+ return "."
+ case "matrix":
+ return ";"
+ case "simple":
+ return ","
+ default:
+ return "&"
+ }
+}
+
+export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
+ switch (style) {
+ case "form":
+ return ","
+ case "pipeDelimited":
+ return "|"
+ case "spaceDelimited":
+ return "%20"
+ default:
+ return ","
+ }
+}
+
+export const separatorObjectExplode = (style: ObjectSeparatorStyle) => {
+ switch (style) {
+ case "label":
+ return "."
+ case "matrix":
+ return ";"
+ case "simple":
+ return ","
+ default:
+ return "&"
+ }
+}
+
+export const serializeArrayParam = ({
+ allowReserved,
+ explode,
+ name,
+ style,
+ value,
+}: SerializeOptions<ArraySeparatorStyle> & {
+ value: unknown[]
+}) => {
+ if (!explode) {
+ const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v as string))).join(
+ separatorArrayNoExplode(style),
+ )
+ switch (style) {
+ case "label":
+ return `.${joinedValues}`
+ case "matrix":
+ return `;${name}=${joinedValues}`
+ case "simple":
+ return joinedValues
+ default:
+ return `${name}=${joinedValues}`
+ }
+ }
+
+ const separator = separatorArrayExplode(style)
+ const joinedValues = value
+ .map((v) => {
+ if (style === "label" || style === "simple") {
+ return allowReserved ? v : encodeURIComponent(v as string)
+ }
+
+ return serializePrimitiveParam({
+ allowReserved,
+ name,
+ value: v as string,
+ })
+ })
+ .join(separator)
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues
+}
+
+export const serializePrimitiveParam = ({ allowReserved, name, value }: SerializePrimitiveParam) => {
+ if (value === undefined || value === null) {
+ return ""
+ }
+
+ if (typeof value === "object") {
+ throw new Error(
+ "Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.",
+ )
+ }
+
+ return `${name}=${allowReserved ? value : encodeURIComponent(value)}`
+}
+
+export const serializeObjectParam = ({
+ allowReserved,
+ explode,
+ name,
+ style,
+ value,
+ valueOnly,
+}: SerializeOptions<ObjectSeparatorStyle> & {
+ value: Record<string, unknown> | Date
+ valueOnly?: boolean
+}) => {
+ if (value instanceof Date) {
+ return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`
+ }
+
+ if (style !== "deepObject" && !explode) {
+ let values: string[] = []
+ Object.entries(value).forEach(([key, v]) => {
+ values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)]
+ })
+ const joinedValues = values.join(",")
+ switch (style) {
+ case "form":
+ return `${name}=${joinedValues}`
+ case "label":
+ return `.${joinedValues}`
+ case "matrix":
+ return `;${name}=${joinedValues}`
+ default:
+ return joinedValues
+ }
+ }
+
+ const separator = separatorObjectExplode(style)
+ const joinedValues = Object.entries(value)
+ .map(([key, v]) =>
+ serializePrimitiveParam({
+ allowReserved,
+ name: style === "deepObject" ? `${name}[${key}]` : key,
+ value: v as string,
+ }),
+ )
+ .join(separator)
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues
+}
diff --git a/packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts b/packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts
new file mode 100644
index 000000000..320204aef
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/queryKeySerializer.gen.ts
@@ -0,0 +1,111 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+/**
+ * JSON-friendly union that mirrors what Pinia Colada can hash.
+ */
+export type JsonValue = null | string | number | boolean | JsonValue[] | { [key: string]: JsonValue }
+
+/**
+ * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes.
+ */
+export const queryKeyJsonReplacer = (_key: string, value: unknown) => {
+ if (value === undefined || typeof value === "function" || typeof value === "symbol") {
+ return undefined
+ }
+ if (typeof value === "bigint") {
+ return value.toString()
+ }
+ if (value instanceof Date) {
+ return value.toISOString()
+ }
+ return value
+}
+
+/**
+ * Safely stringifies a value and parses it back into a JsonValue.
+ */
+export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => {
+ try {
+ const json = JSON.stringify(input, queryKeyJsonReplacer)
+ if (json === undefined) {
+ return undefined
+ }
+ return JSON.parse(json) as JsonValue
+ } catch {
+ return undefined
+ }
+}
+
+/**
+ * Detects plain objects (including objects with a null prototype).
+ */
+const isPlainObject = (value: unknown): value is Record<string, unknown> => {
+ if (value === null || typeof value !== "object") {
+ return false
+ }
+ const prototype = Object.getPrototypeOf(value as object)
+ return prototype === Object.prototype || prototype === null
+}
+
+/**
+ * Turns URLSearchParams into a sorted JSON object for deterministic keys.
+ */
+const serializeSearchParams = (params: URLSearchParams): JsonValue => {
+ const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b))
+ const result: Record<string, JsonValue> = {}
+
+ for (const [key, value] of entries) {
+ const existing = result[key]
+ if (existing === undefined) {
+ result[key] = value
+ continue
+ }
+
+ if (Array.isArray(existing)) {
+ ;(existing as string[]).push(value)
+ } else {
+ result[key] = [existing, value]
+ }
+ }
+
+ return result
+}
+
+/**
+ * Normalizes any accepted value into a JSON-friendly shape for query keys.
+ */
+export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => {
+ if (value === null) {
+ return null
+ }
+
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
+ return value
+ }
+
+ if (value === undefined || typeof value === "function" || typeof value === "symbol") {
+ return undefined
+ }
+
+ if (typeof value === "bigint") {
+ return value.toString()
+ }
+
+ if (value instanceof Date) {
+ return value.toISOString()
+ }
+
+ if (Array.isArray(value)) {
+ return stringifyToJsonValue(value)
+ }
+
+ if (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) {
+ return serializeSearchParams(value)
+ }
+
+ if (isPlainObject(value)) {
+ return stringifyToJsonValue(value)
+ }
+
+ return undefined
+}
diff --git a/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts b/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts
new file mode 100644
index 000000000..09ef3fb39
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/serverSentEvents.gen.ts
@@ -0,0 +1,237 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { Config } from "./types.gen.js"
+
+export type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, "method"> &
+ Pick<Config, "method" | "responseTransformer" | "responseValidator"> & {
+ /**
+ * Fetch API implementation. You can use this option to provide a custom
+ * fetch instance.
+ *
+ * @default globalThis.fetch
+ */
+ fetch?: typeof fetch
+ /**
+ * Implementing clients can call request interceptors inside this hook.
+ */
+ onRequest?: (url: string, init: RequestInit) => Promise<Request>
+ /**
+ * Callback invoked when a network or parsing error occurs during streaming.
+ *
+ * This option applies only if the endpoint returns a stream of events.
+ *
+ * @param error The error that occurred.
+ */
+ onSseError?: (error: unknown) => void
+ /**
+ * Callback invoked when an event is streamed from the server.
+ *
+ * This option applies only if the endpoint returns a stream of events.
+ *
+ * @param event Event streamed from the server.
+ * @returns Nothing (void).
+ */
+ onSseEvent?: (event: StreamEvent<TData>) => void
+ serializedBody?: RequestInit["body"]
+ /**
+ * Default retry delay in milliseconds.
+ *
+ * This option applies only if the endpoint returns a stream of events.
+ *
+ * @default 3000
+ */
+ sseDefaultRetryDelay?: number
+ /**
+ * Maximum number of retry attempts before giving up.
+ */
+ sseMaxRetryAttempts?: number
+ /**
+ * Maximum retry delay in milliseconds.
+ *
+ * Applies only when exponential backoff is used.
+ *
+ * This option applies only if the endpoint returns a stream of events.
+ *
+ * @default 30000
+ */
+ sseMaxRetryDelay?: number
+ /**
+ * Optional sleep function for retry backoff.
+ *
+ * Defaults to using `setTimeout`.
+ */
+ sseSleepFn?: (ms: number) => Promise<void>
+ url: string
+ }
+
+export interface StreamEvent<TData = unknown> {
+ data: TData
+ event?: string
+ id?: string
+ retry?: number
+}
+
+export type ServerSentEventsResult<TData = unknown, TReturn = void, TNext = unknown> = {
+ stream: AsyncGenerator<TData extends Record<string, unknown> ? TData[keyof TData] : TData, TReturn, TNext>
+}
+
+export const createSseClient = <TData = unknown>({
+ onRequest,
+ onSseError,
+ onSseEvent,
+ responseTransformer,
+ responseValidator,
+ sseDefaultRetryDelay,
+ sseMaxRetryAttempts,
+ sseMaxRetryDelay,
+ sseSleepFn,
+ url,
+ ...options
+}: ServerSentEventsOptions): ServerSentEventsResult<TData> => {
+ let lastEventId: string | undefined
+
+ const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)))
+
+ const createStream = async function* () {
+ let retryDelay: number = sseDefaultRetryDelay ?? 3000
+ let attempt = 0
+ const signal = options.signal ?? new AbortController().signal
+
+ while (true) {
+ if (signal.aborted) break
+
+ attempt++
+
+ const headers =
+ options.headers instanceof Headers
+ ? options.headers
+ : new Headers(options.headers as Record<string, string> | undefined)
+
+ if (lastEventId !== undefined) {
+ headers.set("Last-Event-ID", lastEventId)
+ }
+
+ try {
+ const requestInit: RequestInit = {
+ redirect: "follow",
+ ...options,
+ body: options.serializedBody,
+ headers,
+ signal,
+ }
+ let request = new Request(url, requestInit)
+ if (onRequest) {
+ request = await onRequest(url, requestInit)
+ }
+ // fetch must be assigned here, otherwise it would throw the error:
+ // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
+ const _fetch = options.fetch ?? globalThis.fetch
+ const response = await _fetch(request)
+
+ if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`)
+
+ if (!response.body) throw new Error("No body in SSE response")
+
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()
+
+ let buffer = ""
+
+ const abortHandler = () => {
+ try {
+ reader.cancel()
+ } catch {
+ // noop
+ }
+ }
+
+ signal.addEventListener("abort", abortHandler)
+
+ try {
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+ buffer += value
+
+ const chunks = buffer.split("\n\n")
+ buffer = chunks.pop() ?? ""
+
+ for (const chunk of chunks) {
+ const lines = chunk.split("\n")
+ const dataLines: Array<string> = []
+ let eventName: string | undefined
+
+ for (const line of lines) {
+ if (line.startsWith("data:")) {
+ dataLines.push(line.replace(/^data:\s*/, ""))
+ } else if (line.startsWith("event:")) {
+ eventName = line.replace(/^event:\s*/, "")
+ } else if (line.startsWith("id:")) {
+ lastEventId = line.replace(/^id:\s*/, "")
+ } else if (line.startsWith("retry:")) {
+ const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10)
+ if (!Number.isNaN(parsed)) {
+ retryDelay = parsed
+ }
+ }
+ }
+
+ let data: unknown
+ let parsedJson = false
+
+ if (dataLines.length) {
+ const rawData = dataLines.join("\n")
+ try {
+ data = JSON.parse(rawData)
+ parsedJson = true
+ } catch {
+ data = rawData
+ }
+ }
+
+ if (parsedJson) {
+ if (responseValidator) {
+ await responseValidator(data)
+ }
+
+ if (responseTransformer) {
+ data = await responseTransformer(data)
+ }
+ }
+
+ onSseEvent?.({
+ data,
+ event: eventName,
+ id: lastEventId,
+ retry: retryDelay,
+ })
+
+ if (dataLines.length) {
+ yield data as any
+ }
+ }
+ }
+ } finally {
+ signal.removeEventListener("abort", abortHandler)
+ reader.releaseLock()
+ }
+
+ break // exit loop on normal completion
+ } catch (error) {
+ // connection failed or aborted; retry after delay
+ onSseError?.(error)
+
+ if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) {
+ break // stop after firing error
+ }
+
+ // exponential backoff: double retry each attempt, cap at 30s
+ const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000)
+ await sleep(backoff)
+ }
+ }
+ }
+
+ const stream = createStream()
+
+ return { stream }
+}
diff --git a/packages/sdk/js/src/v2/gen/core/types.gen.ts b/packages/sdk/js/src/v2/gen/core/types.gen.ts
new file mode 100644
index 000000000..bfa77b8ac
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/types.gen.ts
@@ -0,0 +1,86 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { Auth, AuthToken } from "./auth.gen.js"
+import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.gen.js"
+
+export type HttpMethod = "connect" | "delete" | "get" | "head" | "options" | "patch" | "post" | "put" | "trace"
+
+export type Client<RequestFn = never, Config = unknown, MethodFn = never, BuildUrlFn = never, SseFn = never> = {
+ /**
+ * Returns the final request URL.
+ */
+ buildUrl: BuildUrlFn
+ getConfig: () => Config
+ request: RequestFn
+ setConfig: (config: Config) => Config
+} & {
+ [K in HttpMethod]: MethodFn
+} & ([SseFn] extends [never] ? { sse?: never } : { sse: { [K in HttpMethod]: SseFn } })
+
+export interface Config {
+ /**
+ * Auth token or a function returning auth token. The resolved value will be
+ * added to the request payload as defined by its `security` array.
+ */
+ auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken
+ /**
+ * A function for serializing request body parameter. By default,
+ * {@link JSON.stringify()} will be used.
+ */
+ bodySerializer?: BodySerializer | null
+ /**
+ * An object containing any HTTP headers that you want to pre-populate your
+ * `Headers` object with.
+ *
+ * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
+ */
+ headers?:
+ | RequestInit["headers"]
+ | Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined | unknown>
+ /**
+ * The request method.
+ *
+ * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
+ */
+ method?: Uppercase<HttpMethod>
+ /**
+ * A function for serializing request query parameters. By default, arrays
+ * will be exploded in form style, objects will be exploded in deepObject
+ * style, and reserved characters are percent-encoded.
+ *
+ * This method will have no effect if the native `paramsSerializer()` Axios
+ * API function is used.
+ *
+ * {@link https://swagger.io/docs/specification/serialization/#query View examples}
+ */
+ querySerializer?: QuerySerializer | QuerySerializerOptions
+ /**
+ * 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 server.
+ */
+ requestValidator?: (data: unknown) => Promise<unknown>
+ /**
+ * A function transforming response data before it's returned. This is useful
+ * for post-processing data, e.g. converting ISO strings into Date objects.
+ */
+ responseTransformer?: (data: unknown) => Promise<unknown>
+ /**
+ * 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 transformers and returned to the user.
+ */
+ responseValidator?: (data: unknown) => Promise<unknown>
+}
+
+type IsExactlyNeverOrNeverUndefined<T> = [T] extends [never]
+ ? true
+ : [T] extends [never | undefined]
+ ? [undefined] extends [T]
+ ? false
+ : true
+ : false
+
+export type OmitNever<T extends Record<string, unknown>> = {
+ [K in keyof T as IsExactlyNeverOrNeverUndefined<T[K]> extends true ? never : K]: T[K]
+}
diff --git a/packages/sdk/js/src/v2/gen/core/utils.gen.ts b/packages/sdk/js/src/v2/gen/core/utils.gen.ts
new file mode 100644
index 000000000..8a45f7269
--- /dev/null
+++ b/packages/sdk/js/src/v2/gen/core/utils.gen.ts
@@ -0,0 +1,137 @@
+// This file is auto-generated by @hey-api/openapi-ts
+
+import type { BodySerializer, QuerySerializer } from "./bodySerializer.gen.js"
+import {
+ type ArraySeparatorStyle,
+ serializeArrayParam,
+ serializeObjectParam,
+ serializePrimitiveParam,
+} from "./pathSerializer.gen.js"
+
+export interface PathSerializer {
+ path: Record<string, unknown>
+ url: string
+}
+
+export const PATH_PARAM_RE = /\{[^{}]+\}/g
+
+export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
+ let url = _url
+ const matches = _url.match(PATH_PARAM_RE)
+ if (matches) {
+ for (const match of matches) {
+ let explode = false
+ let name = match.substring(1, match.length - 1)
+ let style: ArraySeparatorStyle = "simple"
+
+ if (name.endsWith("*")) {
+ explode = true
+ name = name.substring(0, name.length - 1)
+ }
+
+ if (name.startsWith(".")) {
+ name = name.substring(1)
+ style = "label"
+ } else if (name.startsWith(";")) {
+ name = name.substring(1)
+ style = "matrix"
+ }
+
+ const value = path[name]
+
+ if (value === undefined || value === null) {
+ continue
+ }
+
+ if (Array.isArray(value)) {
+ url = url.replace(match, serializeArrayParam({ explode, name, style, value }))
+ continue
+ }
+
+ if (typeof value === "object") {
+ url = url.replace(
+ match,
+ serializeObjectParam({
+ explode,
+ name,
+ style,
+ value: value as Record<string, unknown>,
+ valueOnly: true,
+ }),
+ )
+ continue
+ }
+
+ if (style === "matrix") {
+ url = url.replace(
+ match,
+ `;${serializePrimitiveParam({
+ name,
+ value: value as string,
+ })}`,
+ )
+ continue
+ }
+
+ const replaceValue = encodeURIComponent(style === "label" ? `.${value as string}` : (value as string))
+ url = url.replace(match, replaceValue)
+ }
+ }
+ return url
+}
+
+export const getUrl = ({
+ baseUrl,
+ path,
+ query,
+ querySerializer,
+ url: _url,
+}: {
+ baseUrl?: string
+ path?: Record<string, unknown>
+ query?: Record<string, unknown>
+ querySerializer: QuerySerializer
+ url: string
+}) => {
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`
+ let url = (baseUrl ?? "") + pathUrl
+ if (path) {
+ url = defaultPathSerializer({ path, url })
+ }
+ let search = query ? querySerializer(query) : ""
+ if (search.startsWith("?")) {
+ search = search.substring(1)
+ }
+ if (search) {
+ url += `?${search}`
+ }
+ return url
+}
+
+export function getValidRequestBody(options: {
+ body?: unknown
+ bodySerializer?: BodySerializer | null
+ serializedBody?: unknown
+}) {
+ const hasBody = options.body !== undefined
+ const isSerializedBody = hasBody && options.bodySerializer
+
+ if (isSerializedBody) {
+ if ("serializedBody" in options) {
+ const hasSerializedBody = options.serializedBody !== undefined && options.serializedBody !== ""
+
+ return hasSerializedBody ? options.serializedBody : null
+ }
+
+ // not all clients implement a serializedBody property (i.e. client-axios)
+ return options.body !== "" ? options.body : null
+ }
+
+ // plain/text body
+ if (hasBody) {
+ return options.body
+ }
+
+ // no body was provided
+ return undefined
+}