From 732b67f8ce65ca8154574088abfbce06dd5054bb Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Sat, 23 Aug 2025 12:21:58 -0400 Subject: ci: stuff --- packages/sdk/js/src/client.ts | 4 +- packages/sdk/js/src/gen/client/client.gen.ts | 212 ------------ packages/sdk/js/src/gen/client/client.ts | 185 ++++++++++ packages/sdk/js/src/gen/client/index.ts | 21 +- packages/sdk/js/src/gen/client/types.gen.ts | 222 ------------ packages/sdk/js/src/gen/client/types.ts | 191 +++++++++++ packages/sdk/js/src/gen/client/utils.gen.ts | 287 ---------------- packages/sdk/js/src/gen/client/utils.ts | 373 +++++++++++++++++++++ packages/sdk/js/src/gen/core/auth.gen.ts | 41 --- packages/sdk/js/src/gen/core/auth.ts | 39 +++ packages/sdk/js/src/gen/core/bodySerializer.gen.ts | 74 ---- packages/sdk/js/src/gen/core/bodySerializer.ts | 70 ++++ packages/sdk/js/src/gen/core/params.gen.ts | 144 -------- packages/sdk/js/src/gen/core/params.ts | 142 ++++++++ packages/sdk/js/src/gen/core/pathSerializer.gen.ts | 167 --------- packages/sdk/js/src/gen/core/pathSerializer.ts | 165 +++++++++ .../sdk/js/src/gen/core/serverSentEvents.gen.ts | 210 ------------ packages/sdk/js/src/gen/core/types.gen.ts | 91 ----- packages/sdk/js/src/gen/core/types.ts | 89 +++++ packages/sdk/js/src/gen/core/utils.gen.ts | 109 ------ packages/sdk/js/src/gen/sdk.gen.ts | 2 +- 21 files changed, 1264 insertions(+), 1574 deletions(-) delete mode 100644 packages/sdk/js/src/gen/client/client.gen.ts create mode 100644 packages/sdk/js/src/gen/client/client.ts delete mode 100644 packages/sdk/js/src/gen/client/types.gen.ts create mode 100644 packages/sdk/js/src/gen/client/types.ts delete mode 100644 packages/sdk/js/src/gen/client/utils.gen.ts create mode 100644 packages/sdk/js/src/gen/client/utils.ts delete mode 100644 packages/sdk/js/src/gen/core/auth.gen.ts create mode 100644 packages/sdk/js/src/gen/core/auth.ts delete mode 100644 packages/sdk/js/src/gen/core/bodySerializer.gen.ts create mode 100644 packages/sdk/js/src/gen/core/bodySerializer.ts delete mode 100644 packages/sdk/js/src/gen/core/params.gen.ts create mode 100644 packages/sdk/js/src/gen/core/params.ts delete mode 100644 packages/sdk/js/src/gen/core/pathSerializer.gen.ts create mode 100644 packages/sdk/js/src/gen/core/pathSerializer.ts delete mode 100644 packages/sdk/js/src/gen/core/serverSentEvents.gen.ts delete mode 100644 packages/sdk/js/src/gen/core/types.gen.ts create mode 100644 packages/sdk/js/src/gen/core/types.ts delete mode 100644 packages/sdk/js/src/gen/core/utils.gen.ts diff --git a/packages/sdk/js/src/client.ts b/packages/sdk/js/src/client.ts index 29b9de906..8346fd8a2 100644 --- a/packages/sdk/js/src/client.ts +++ b/packages/sdk/js/src/client.ts @@ -1,8 +1,8 @@ export * from "./gen/types.gen.js" export { type Config as OpencodeClientConfig, OpencodeClient } -import { createClient } from "./gen/client/client.gen.js" -import { type Config } from "./gen/client/types.gen.js" +import { createClient } from "./gen/client/client.js" +import { type Config } from "./gen/client/types.js" import { OpencodeClient } from "./gen/sdk.gen.js" export function createOpencodeClient(config?: Config) { diff --git a/packages/sdk/js/src/gen/client/client.gen.ts b/packages/sdk/js/src/gen/client/client.gen.ts deleted file mode 100644 index 34a8d0bec..000000000 --- a/packages/sdk/js/src/gen/client/client.gen.ts +++ /dev/null @@ -1,212 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { createSseClient } from "../core/serverSentEvents.gen.js" -import type { Client, Config, RequestOptions, ResolvedRequestOptions } from "./types.gen.js" -import { - buildUrl, - createConfig, - createInterceptors, - getParseAs, - mergeConfigs, - mergeHeaders, - setAuthParams, -} from "./utils.gen.js" - -type ReqInit = Omit & { - body?: any - headers: ReturnType -} - -export const createClient = (config: Config = {}): Client => { - let _config = mergeConfigs(createConfig(), config) - - const getConfig = (): Config => ({ ..._config }) - - const setConfig = (config: Config): Config => { - _config = mergeConfigs(_config, config) - return getConfig() - } - - const interceptors = createInterceptors() - - const beforeRequest = async (options: RequestOptions) => { - const opts = { - ..._config, - ...options, - fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, - headers: mergeHeaders(_config.headers, options.headers), - serializedBody: undefined, - } - - if (opts.security) { - await setAuthParams({ - ...opts, - security: opts.security, - }) - } - - if (opts.requestValidator) { - await opts.requestValidator(opts) - } - - if (opts.body && opts.bodySerializer) { - opts.serializedBody = opts.bodySerializer(opts.body) - } - - // remove Content-Type header if body is empty to avoid sending invalid requests - if (opts.serializedBody === undefined || opts.serializedBody === "") { - opts.headers.delete("Content-Type") - } - - const url = buildUrl(opts) - - return { opts, url } - } - - const request: Client["request"] = async (options) => { - // @ts-expect-error - const { opts, url } = await beforeRequest(options) - const requestInit: ReqInit = { - redirect: "follow", - ...opts, - body: opts.serializedBody, - } - - let request = new Request(url, requestInit) - - for (const fn of interceptors.request._fns) { - if (fn) { - request = await fn(request, opts) - } - } - - // fetch must be assigned here, otherwise it would throw the error: - // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation - const _fetch = opts.fetch! - let response = await _fetch(request) - - for (const fn of interceptors.response._fns) { - if (fn) { - response = await fn(response, request, opts) - } - } - - const result = { - request, - response, - } - - if (response.ok) { - if (response.status === 204 || response.headers.get("Content-Length") === "0") { - return opts.responseStyle === "data" - ? {} - : { - data: {}, - ...result, - } - } - - const parseAs = - (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json" - - let data: any - switch (parseAs) { - case "arrayBuffer": - case "blob": - case "formData": - case "json": - case "text": - data = await response[parseAs]() - break - case "stream": - return opts.responseStyle === "data" - ? response.body - : { - data: response.body, - ...result, - } - } - - if (parseAs === "json") { - if (opts.responseValidator) { - await opts.responseValidator(data) - } - - if (opts.responseTransformer) { - data = await opts.responseTransformer(data) - } - } - - return opts.responseStyle === "data" - ? data - : { - data, - ...result, - } - } - - const textError = await response.text() - let jsonError: unknown - - try { - jsonError = JSON.parse(textError) - } catch { - // noop - } - - const error = jsonError ?? textError - let finalError = error - - for (const fn of interceptors.error._fns) { - if (fn) { - finalError = (await fn(error, response, request, opts)) as string - } - } - - finalError = finalError || ({} as string) - - if (opts.throwOnError) { - throw finalError - } - - // TODO: we probably want to return error and improve types - return opts.responseStyle === "data" - ? undefined - : { - error: finalError, - ...result, - } - } - - const makeMethod = (method: Required["method"]) => { - const fn = (options: RequestOptions) => request({ ...options, method }) - fn.sse = async (options: RequestOptions) => { - const { opts, url } = await beforeRequest(options) - return createSseClient({ - ...opts, - body: opts.body as BodyInit | null | undefined, - headers: opts.headers as unknown as Record, - method, - url, - }) - } - return fn - } - - return { - buildUrl, - connect: makeMethod("CONNECT"), - delete: makeMethod("DELETE"), - get: makeMethod("GET"), - getConfig, - head: makeMethod("HEAD"), - interceptors, - options: makeMethod("OPTIONS"), - patch: makeMethod("PATCH"), - post: makeMethod("POST"), - put: makeMethod("PUT"), - request, - setConfig, - trace: makeMethod("TRACE"), - } as Client -} diff --git a/packages/sdk/js/src/gen/client/client.ts b/packages/sdk/js/src/gen/client/client.ts new file mode 100644 index 000000000..46a62694c --- /dev/null +++ b/packages/sdk/js/src/gen/client/client.ts @@ -0,0 +1,185 @@ +import type { Client, Config, RequestOptions } from "./types.js" +import { + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from "./utils.js" + +type ReqInit = Omit & { + body?: any + headers: ReturnType +} + +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config) + + const getConfig = (): Config => ({ ..._config }) + + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config) + return getConfig() + } + + const interceptors = createInterceptors() + + const request: Client["request"] = async (options) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + } + + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }) + } + + if (opts.requestValidator) { + await opts.requestValidator(opts) + } + + if (opts.body && opts.bodySerializer) { + opts.body = opts.bodySerializer(opts.body) + } + + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.body === undefined || opts.body === "") { + opts.headers.delete("Content-Type") + } + + const url = buildUrl(opts) + const requestInit: ReqInit = { + redirect: "follow", + ...opts, + } + + let request = new Request(url, requestInit) + + for (const fn of interceptors.request._fns) { + if (fn) { + request = await fn(request, opts) + } + } + + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch! + let response = await _fetch(request) + + for (const fn of interceptors.response._fns) { + if (fn) { + response = await fn(response, request, opts) + } + } + + const result = { + request, + response, + } + + if (response.ok) { + if (response.status === 204 || response.headers.get("Content-Length") === "0") { + return opts.responseStyle === "data" + ? {} + : { + data: {}, + ...result, + } + } + + const parseAs = + (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json" + + let data: any + switch (parseAs) { + case "arrayBuffer": + case "blob": + case "formData": + case "json": + case "text": + data = await response[parseAs]() + break + case "stream": + return opts.responseStyle === "data" + ? response.body + : { + data: response.body, + ...result, + } + } + + if (parseAs === "json") { + if (opts.responseValidator) { + await opts.responseValidator(data) + } + + if (opts.responseTransformer) { + data = await opts.responseTransformer(data) + } + } + + return opts.responseStyle === "data" + ? data + : { + data, + ...result, + } + } + + const textError = await response.text() + let jsonError: unknown + + try { + jsonError = JSON.parse(textError) + } catch { + // noop + } + + const error = jsonError ?? textError + let finalError = error + + for (const fn of interceptors.error._fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string + } + } + + finalError = finalError || ({} as string) + + if (opts.throwOnError) { + throw finalError + } + + // TODO: we probably want to return error and improve types + return opts.responseStyle === "data" + ? undefined + : { + error: finalError, + ...result, + } + } + + return { + buildUrl, + connect: (options) => request({ ...options, method: "CONNECT" }), + delete: (options) => request({ ...options, method: "DELETE" }), + get: (options) => request({ ...options, method: "GET" }), + getConfig, + head: (options) => request({ ...options, method: "HEAD" }), + interceptors, + options: (options) => request({ ...options, method: "OPTIONS" }), + patch: (options) => request({ ...options, method: "PATCH" }), + post: (options) => request({ ...options, method: "POST" }), + put: (options) => request({ ...options, method: "PUT" }), + request, + setConfig, + trace: (options) => request({ ...options, method: "TRACE" }), + } +} diff --git a/packages/sdk/js/src/gen/client/index.ts b/packages/sdk/js/src/gen/client/index.ts index 06f21e3d8..ce89a34cc 100644 --- a/packages/sdk/js/src/gen/client/index.ts +++ b/packages/sdk/js/src/gen/client/index.ts @@ -1,14 +1,8 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type { Auth } from "../core/auth.gen.js" -export type { QuerySerializerOptions } from "../core/bodySerializer.gen.js" -export { - formDataBodySerializer, - jsonBodySerializer, - urlSearchParamsBodySerializer, -} from "../core/bodySerializer.gen.js" -export { buildClientParams } from "../core/params.gen.js" -export { createClient } from "./client.gen.js" +export type { Auth } from "../core/auth.js" +export type { QuerySerializerOptions } from "../core/bodySerializer.js" +export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer } from "../core/bodySerializer.js" +export { buildClientParams } from "../core/params.js" +export { createClient } from "./client.js" export type { Client, ClientOptions, @@ -18,8 +12,7 @@ export type { OptionsLegacyParser, RequestOptions, RequestResult, - ResolvedRequestOptions, ResponseStyle, TDataShape, -} from "./types.gen.js" -export { createConfig, mergeHeaders } from "./utils.gen.js" +} from "./types.js" +export { createConfig, mergeHeaders } from "./utils.js" diff --git a/packages/sdk/js/src/gen/client/types.gen.ts b/packages/sdk/js/src/gen/client/types.gen.ts deleted file mode 100644 index db8e544cf..000000000 --- a/packages/sdk/js/src/gen/client/types.gen.ts +++ /dev/null @@ -1,222 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { Auth } from "../core/auth.gen.js" -import type { ServerSentEventsOptions, ServerSentEventsResult } 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 interface Config - extends Omit, - CoreConfig { - /** - * Base URL for all requests made by this client. - */ - baseUrl?: T["baseUrl"] - /** - * Fetch API implementation. You can use this option to provide a custom - * fetch instance. - * - * @default globalThis.fetch - */ - fetch?: (request: Request) => ReturnType - /** - * Please don't use the Fetch client for Next.js applications. The `next` - * options won't have any effect. - * - * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. - */ - next?: never - /** - * Return the response data parsed in a specified format. By default, `auto` - * will infer the appropriate method from the `Content-Type` response header. - * You can override this behavior with any of the {@link Body} methods. - * Select `stream` if you don't want to parse response data at all. - * - * @default 'auto' - */ - parseAs?: "arrayBuffer" | "auto" | "blob" | "formData" | "json" | "stream" | "text" - /** - * Should we return only data or multiple fields (data, error, response, etc.)? - * - * @default 'fields' - */ - responseStyle?: ResponseStyle - /** - * Throw an error instead of returning it in the response? - * - * @default false - */ - throwOnError?: T["throwOnError"] -} - -export interface RequestOptions< - TData = unknown, - TResponseStyle extends ResponseStyle = "fields", - ThrowOnError extends boolean = boolean, - Url extends string = string, -> extends Config<{ - responseStyle: TResponseStyle - throwOnError: ThrowOnError - }>, - Pick< - ServerSentEventsOptions, - "onSseError" | "onSseEvent" | "sseDefaultRetryDelay" | "sseMaxRetryAttempts" | "sseMaxRetryDelay" - > { - /** - * Any body that you want to add to your request. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} - */ - body?: unknown - path?: Record - query?: Record - /** - * Security mechanism(s) to use for the request. - */ - security?: ReadonlyArray - url: Url -} - -export interface ResolvedRequestOptions< - TResponseStyle extends ResponseStyle = "fields", - ThrowOnError extends boolean = boolean, - Url extends string = string, -> extends RequestOptions { - serializedBody?: string -} - -export type RequestResult< - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = boolean, - TResponseStyle extends ResponseStyle = "fields", -> = ThrowOnError extends true - ? Promise< - TResponseStyle extends "data" - ? TData extends Record - ? TData[keyof TData] - : TData - : { - data: TData extends Record ? TData[keyof TData] : TData - request: Request - response: Response - } - > - : Promise< - TResponseStyle extends "data" - ? (TData extends Record ? TData[keyof TData] : TData) | undefined - : ( - | { - data: TData extends Record ? TData[keyof TData] : TData - error: undefined - } - | { - data: undefined - error: TError extends Record ? TError[keyof TError] : TError - } - ) & { - request: Request - response: Response - } - > - -export interface ClientOptions { - baseUrl?: string - responseStyle?: ResponseStyle - throwOnError?: boolean -} - -type MethodFnBase = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = "fields", ->( - options: Omit, "method">, -) => RequestResult - -type MethodFnServerSentEvents = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = "fields", ->( - options: Omit, "method">, -) => Promise> - -type MethodFn = MethodFnBase & { - sse: MethodFnServerSentEvents -} - -type RequestFn = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = "fields", ->( - options: Omit, "method"> & - Pick>, "method">, -) => RequestResult - -type BuildUrlFn = < - TData extends { - body?: unknown - path?: Record - query?: Record - url: string - }, ->( - options: Pick & Options, -) => string - -export type Client = CoreClient & { - interceptors: Middleware -} - -/** - * The `createClientConfig()` function will be called on client initialization - * and the returned object will become the client's initial configuration. - * - * You may want to initialize your client this way instead of calling - * `setConfig()`. This is useful for example if you're using Next.js - * to ensure your client always has the correct values. - */ -export type CreateClientConfig = ( - override?: Config, -) => Config & T> - -export interface TDataShape { - body?: unknown - headers?: unknown - path?: unknown - query?: unknown - url: string -} - -type OmitKeys = Pick> - -export type Options< - TData extends TDataShape = TDataShape, - ThrowOnError extends boolean = boolean, - TResponse = unknown, - TResponseStyle extends ResponseStyle = "fields", -> = OmitKeys, "body" | "path" | "query" | "url"> & - Omit - -export type OptionsLegacyParser< - TData = unknown, - ThrowOnError extends boolean = boolean, - TResponseStyle extends ResponseStyle = "fields", -> = TData extends { body?: any } - ? TData extends { headers?: any } - ? OmitKeys, "body" | "headers" | "url"> & TData - : OmitKeys, "body" | "url"> & - TData & - Pick, "headers"> - : TData extends { headers?: any } - ? OmitKeys, "headers" | "url"> & - TData & - Pick, "body"> - : OmitKeys, "url"> & TData diff --git a/packages/sdk/js/src/gen/client/types.ts b/packages/sdk/js/src/gen/client/types.ts new file mode 100644 index 000000000..f3b116bae --- /dev/null +++ b/packages/sdk/js/src/gen/client/types.ts @@ -0,0 +1,191 @@ +import type { Auth } from "../core/auth.js" +import type { Client as CoreClient, Config as CoreConfig } from "../core/types.js" +import type { Middleware } from "./utils.js" + +export type ResponseStyle = "data" | "fields" + +export interface Config + extends Omit, + CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T["baseUrl"] + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: (request: Request) => ReturnType + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: "arrayBuffer" | "auto" | "blob" | "formData" | "json" | "stream" | "text" + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T["throwOnError"] +} + +export interface RequestOptions< + TResponseStyle extends ResponseStyle = "fields", + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends Config<{ + responseStyle: TResponseStyle + throwOnError: ThrowOnError + }> { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown + path?: Record + query?: Record + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray + url: Url +} + +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = "fields", +> = ThrowOnError extends true + ? Promise< + TResponseStyle extends "data" + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record ? TData[keyof TData] : TData + request: Request + response: Response + } + > + : Promise< + TResponseStyle extends "data" + ? (TData extends Record ? TData[keyof TData] : TData) | undefined + : ( + | { + data: TData extends Record ? TData[keyof TData] : TData + error: undefined + } + | { + data: undefined + error: TError extends Record ? TError[keyof TError] : TError + } + ) & { + request: Request + response: Response + } + > + +export interface ClientOptions { + baseUrl?: string + responseStyle?: ResponseStyle + throwOnError?: boolean +} + +type MethodFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = "fields", +>( + options: Omit, "method">, +) => RequestResult + +type RequestFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = "fields", +>( + options: Omit, "method"> & + Pick>, "method">, +) => RequestResult + +type BuildUrlFn = < + TData extends { + body?: unknown + path?: Record + query?: Record + url: string + }, +>( + options: Pick & Options, +) => string + +export type Client = CoreClient & { + interceptors: Middleware +} + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = ( + override?: Config, +) => Config & T> + +export interface TDataShape { + body?: unknown + headers?: unknown + path?: unknown + query?: unknown + url: string +} + +type OmitKeys = Pick> + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = "fields", +> = OmitKeys, "body" | "path" | "query" | "url"> & Omit + +export type OptionsLegacyParser< + TData = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = "fields", +> = TData extends { body?: any } + ? TData extends { headers?: any } + ? OmitKeys, "body" | "headers" | "url"> & TData + : OmitKeys, "body" | "url"> & + TData & + Pick, "headers"> + : TData extends { headers?: any } + ? OmitKeys, "headers" | "url"> & + TData & + Pick, "body"> + : OmitKeys, "url"> & TData diff --git a/packages/sdk/js/src/gen/client/utils.gen.ts b/packages/sdk/js/src/gen/client/utils.gen.ts deleted file mode 100644 index 209bfbe8e..000000000 --- a/packages/sdk/js/src/gen/client/utils.gen.ts +++ /dev/null @@ -1,287 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { getAuthToken } from "../core/auth.gen.js" -import type { QuerySerializerOptions } from "../core/bodySerializer.gen.js" -import { jsonBodySerializer } from "../core/bodySerializer.gen.js" -import { serializeArrayParam, serializeObjectParam, 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 = ({ allowReserved, array, object }: QuerySerializerOptions = {}) => { - const querySerializer = (queryParams: T) => { - const search: string[] = [] - if (queryParams && typeof queryParams === "object") { - for (const name in queryParams) { - const value = queryParams[name] - - if (value === undefined || value === null) { - continue - } - - if (Array.isArray(value)) { - const serializedArray = serializeArrayParam({ - allowReserved, - explode: true, - name, - style: "form", - value, - ...array, - }) - if (serializedArray) search.push(serializedArray) - } else if (typeof value === "object") { - const serializedObject = serializeObjectParam({ - allowReserved, - explode: true, - name, - style: "deepObject", - value: value as Record, - ...object, - }) - if (serializedObject) search.push(serializedObject) - } else { - const serializedPrimitive = serializePrimitiveParam({ - allowReserved, - name, - value: value as string, - }) - if (serializedPrimitive) search.push(serializedPrimitive) - } - } - } - return search.join("&") - } - return querySerializer -} - -/** - * Infers parseAs value from provided Content-Type header. - */ -export const getParseAs = (contentType: string | null): Exclude => { - if (!contentType) { - // 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. - return "stream" - } - - const cleanContent = contentType.split(";")[0]?.trim() - - if (!cleanContent) { - return - } - - if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) { - return "json" - } - - if (cleanContent === "multipart/form-data") { - return "formData" - } - - if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) { - return "blob" - } - - if (cleanContent.startsWith("text/")) { - return "text" - } - - return -} - -const checkForExistence = ( - options: Pick & { - headers: Headers - }, - name?: string, -): boolean => { - if (!name) { - return false - } - if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) { - return true - } - return false -} - -export const setAuthParams = async ({ - security, - ...options -}: Pick, "security"> & - Pick & { - headers: Headers - }) => { - for (const auth of security) { - if (checkForExistence(options, auth.name)) { - continue - } - - const token = await getAuthToken(auth, options.auth) - - if (!token) { - continue - } - - const name = auth.name ?? "Authorization" - - switch (auth.in) { - case "query": - if (!options.query) { - options.query = {} - } - options.query[name] = token - break - case "cookie": - options.headers.append("Cookie", `${name}=${token}`) - break - case "header": - default: - options.headers.set(name, token) - break - } - } -} - -export const buildUrl: Client["buildUrl"] = (options) => - getUrl({ - baseUrl: options.baseUrl as string, - path: options.path, - query: options.query, - querySerializer: - typeof options.querySerializer === "function" - ? options.querySerializer - : createQuerySerializer(options.querySerializer), - url: options.url, - }) - -export const mergeConfigs = (a: Config, b: Config): Config => { - const config = { ...a, ...b } - if (config.baseUrl?.endsWith("/")) { - config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1) - } - config.headers = mergeHeaders(a.headers, b.headers) - return config -} - -export const mergeHeaders = (...headers: Array["headers"] | undefined>): Headers => { - const mergedHeaders = new Headers() - for (const header of headers) { - if (!header || typeof header !== "object") { - continue - } - - const iterator = header instanceof Headers ? header.entries() : Object.entries(header) - - for (const [key, value] of iterator) { - if (value === null) { - mergedHeaders.delete(key) - } else if (Array.isArray(value)) { - for (const v of value) { - mergedHeaders.append(key, v as string) - } - } else if (value !== undefined) { - // assume object headers are meant to be JSON stringified, i.e. their - // content value in OpenAPI specification is 'application/json' - mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : (value as string)) - } - } - } - return mergedHeaders -} - -type ErrInterceptor = ( - error: Err, - response: Res, - request: Req, - options: Options, -) => Err | Promise - -type ReqInterceptor = (request: Req, options: Options) => Req | Promise - -type ResInterceptor = (response: Res, request: Req, options: Options) => Res | Promise - -class Interceptors { - _fns: (Interceptor | null)[] - - constructor() { - this._fns = [] - } - - clear() { - this._fns = [] - } - - getInterceptorIndex(id: number | Interceptor): number { - if (typeof id === "number") { - return this._fns[id] ? id : -1 - } else { - return this._fns.indexOf(id) - } - } - exists(id: number | Interceptor) { - const index = this.getInterceptorIndex(id) - return !!this._fns[index] - } - - eject(id: number | Interceptor) { - const index = this.getInterceptorIndex(id) - if (this._fns[index]) { - this._fns[index] = null - } - } - - update(id: number | Interceptor, fn: Interceptor) { - const index = this.getInterceptorIndex(id) - if (this._fns[index]) { - this._fns[index] = fn - return id - } else { - return false - } - } - - use(fn: Interceptor) { - this._fns = [...this._fns, fn] - return this._fns.length - 1 - } -} - -// `createInterceptors()` response, meant for external use as it does not -// expose internals -export interface Middleware { - error: Pick>, "eject" | "use"> - request: Pick>, "eject" | "use"> - response: Pick>, "eject" | "use"> -} - -// do not add `Middleware` as return type so we can use _fns internally -export const createInterceptors = () => ({ - error: new Interceptors>(), - request: new Interceptors>(), - response: new Interceptors>(), -}) - -const defaultQuerySerializer = createQuerySerializer({ - allowReserved: false, - array: { - explode: true, - style: "form", - }, - object: { - explode: true, - style: "deepObject", - }, -}) - -const defaultHeaders = { - "Content-Type": "application/json", -} - -export const createConfig = ( - override: Config & T> = {}, -): Config & T> => ({ - ...jsonBodySerializer, - headers: defaultHeaders, - parseAs: "auto", - querySerializer: defaultQuerySerializer, - ...override, -}) diff --git a/packages/sdk/js/src/gen/client/utils.ts b/packages/sdk/js/src/gen/client/utils.ts new file mode 100644 index 000000000..84648c855 --- /dev/null +++ b/packages/sdk/js/src/gen/client/utils.ts @@ -0,0 +1,373 @@ +import { getAuthToken } from "../core/auth.js" +import type { QuerySerializer, QuerySerializerOptions } from "../core/bodySerializer.js" +import { jsonBodySerializer } from "../core/bodySerializer.js" +import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from "../core/pathSerializer.js" +import type { Client, ClientOptions, Config, RequestOptions } from "./types.js" + +interface PathSerializer { + path: Record + url: string +} + +const PATH_PARAM_RE = /\{[^{}]+\}/g + +type ArrayStyle = "form" | "spaceDelimited" | "pipeDelimited" +type MatrixStyle = "label" | "matrix" | "simple" +type ArraySeparatorStyle = ArrayStyle | MatrixStyle + +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, + 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 createQuerySerializer = ({ allowReserved, array, object }: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = [] + if (queryParams && typeof queryParams === "object") { + for (const name in queryParams) { + const value = queryParams[name] + + if (value === undefined || value === null) { + continue + } + + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved, + explode: true, + name, + style: "form", + value, + ...array, + }) + if (serializedArray) search.push(serializedArray) + } else if (typeof value === "object") { + const serializedObject = serializeObjectParam({ + allowReserved, + explode: true, + name, + style: "deepObject", + value: value as Record, + ...object, + }) + if (serializedObject) search.push(serializedObject) + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved, + name, + value: value as string, + }) + if (serializedPrimitive) search.push(serializedPrimitive) + } + } + } + return search.join("&") + } + return querySerializer +} + +/** + * Infers parseAs value from provided Content-Type header. + */ +export const getParseAs = (contentType: string | null): Exclude => { + if (!contentType) { + // 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. + return "stream" + } + + const cleanContent = contentType.split(";")[0]?.trim() + + if (!cleanContent) { + return + } + + if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) { + return "json" + } + + if (cleanContent === "multipart/form-data") { + return "formData" + } + + if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) { + return "blob" + } + + if (cleanContent.startsWith("text/")) { + return "text" + } + + return +} + +export const setAuthParams = async ({ + security, + ...options +}: Pick, "security"> & + Pick & { + headers: Headers + }) => { + for (const auth of security) { + const token = await getAuthToken(auth, options.auth) + + if (!token) { + continue + } + + const name = auth.name ?? "Authorization" + + switch (auth.in) { + case "query": + if (!options.query) { + options.query = {} + } + options.query[name] = token + break + case "cookie": + options.headers.append("Cookie", `${name}=${token}`) + break + case "header": + default: + options.headers.set(name, token) + break + } + + return + } +} + +export const buildUrl: Client["buildUrl"] = (options) => { + const url = getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === "function" + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }) + return url +} + +export const getUrl = ({ + baseUrl, + path, + query, + querySerializer, + url: _url, +}: { + baseUrl?: string + path?: Record + query?: Record + 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 const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b } + if (config.baseUrl?.endsWith("/")) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1) + } + config.headers = mergeHeaders(a.headers, b.headers) + return config +} + +export const mergeHeaders = (...headers: Array["headers"] | undefined>): Headers => { + const mergedHeaders = new Headers() + for (const header of headers) { + if (!header || typeof header !== "object") { + continue + } + + const iterator = header instanceof Headers ? header.entries() : Object.entries(header) + + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key) + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string) + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e. their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : (value as string)) + } + } + } + return mergedHeaders +} + +type ErrInterceptor = ( + error: Err, + response: Res, + request: Req, + options: Options, +) => Err | Promise + +type ReqInterceptor = (request: Req, options: Options) => Req | Promise + +type ResInterceptor = (response: Res, request: Req, options: Options) => Res | Promise + +class Interceptors { + _fns: (Interceptor | null)[] + + constructor() { + this._fns = [] + } + + clear() { + this._fns = [] + } + + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === "number") { + return this._fns[id] ? id : -1 + } else { + return this._fns.indexOf(id) + } + } + exists(id: number | Interceptor) { + const index = this.getInterceptorIndex(id) + return !!this._fns[index] + } + + eject(id: number | Interceptor) { + const index = this.getInterceptorIndex(id) + if (this._fns[index]) { + this._fns[index] = null + } + } + + update(id: number | Interceptor, fn: Interceptor) { + const index = this.getInterceptorIndex(id) + if (this._fns[index]) { + this._fns[index] = fn + return id + } else { + return false + } + } + + use(fn: Interceptor) { + this._fns = [...this._fns, fn] + return this._fns.length - 1 + } +} + +// `createInterceptors()` response, meant for external use as it does not +// expose internals +export interface Middleware { + error: Pick>, "eject" | "use"> + request: Pick>, "eject" | "use"> + response: Pick>, "eject" | "use"> +} + +// do not add `Middleware` as return type so we can use _fns internally +export const createInterceptors = () => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), +}) + +const defaultQuerySerializer = createQuerySerializer({ + allowReserved: false, + array: { + explode: true, + style: "form", + }, + object: { + explode: true, + style: "deepObject", + }, +}) + +const defaultHeaders = { + "Content-Type": "application/json", +} + +export const createConfig = ( + override: Config & T> = {}, +): Config & T> => ({ + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: "auto", + querySerializer: defaultQuerySerializer, + ...override, +}) diff --git a/packages/sdk/js/src/gen/core/auth.gen.ts b/packages/sdk/js/src/gen/core/auth.gen.ts deleted file mode 100644 index bc7b230f4..000000000 --- a/packages/sdk/js/src/gen/core/auth.gen.ts +++ /dev/null @@ -1,41 +0,0 @@ -// 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, -): Promise => { - 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/gen/core/auth.ts b/packages/sdk/js/src/gen/core/auth.ts new file mode 100644 index 000000000..e496d4557 --- /dev/null +++ b/packages/sdk/js/src/gen/core/auth.ts @@ -0,0 +1,39 @@ +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, +): Promise => { + 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/gen/core/bodySerializer.gen.ts b/packages/sdk/js/src/gen/core/bodySerializer.gen.ts deleted file mode 100644 index 066061605..000000000 --- a/packages/sdk/js/src/gen/core/bodySerializer.gen.ts +++ /dev/null @@ -1,74 +0,0 @@ -// 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 - -export type BodySerializer = (body: any) => any - -export interface QuerySerializerOptions { - allowReserved?: boolean - array?: SerializerOptions - object?: SerializerOptions -} - -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: | Array>>(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: (body: T): string => - JSON.stringify(body, (_key, value) => (typeof value === "bigint" ? value.toString() : value)), -} - -export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>(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/gen/core/bodySerializer.ts b/packages/sdk/js/src/gen/core/bodySerializer.ts new file mode 100644 index 000000000..8a4a13410 --- /dev/null +++ b/packages/sdk/js/src/gen/core/bodySerializer.ts @@ -0,0 +1,70 @@ +import type { ArrayStyle, ObjectStyle, SerializerOptions } from "./pathSerializer.js" + +export type QuerySerializer = (query: Record) => string + +export type BodySerializer = (body: any) => any + +export interface QuerySerializerOptions { + allowReserved?: boolean + array?: SerializerOptions + object?: SerializerOptions +} + +const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => { + if (typeof value === "string" || value instanceof Blob) { + data.append(key, value) + } 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: | Array>>(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: (body: T): string => + JSON.stringify(body, (_key, value) => (typeof value === "bigint" ? value.toString() : value)), +} + +export const urlSearchParamsBodySerializer = { + bodySerializer: | Array>>(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/gen/core/params.gen.ts b/packages/sdk/js/src/gen/core/params.gen.ts deleted file mode 100644 index 68ad1a778..000000000 --- a/packages/sdk/js/src/gen/core/params.gen.ts +++ /dev/null @@ -1,144 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -type Slot = "body" | "headers" | "path" | "query" - -export type Field = - | { - in: Exclude - /** - * 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 - /** - * Key isn't required for bodies. - */ - key?: string - map?: string - } - -export interface Fields { - allowExtra?: Partial> - args?: ReadonlyArray -} - -export type FieldsConfig = ReadonlyArray - -const extraPrefixesMap: Record = { - $body_: "body", - $headers_: "headers", - $path_: "path", - $query_: "query", -} -const extraPrefixes = Object.entries(extraPrefixesMap) - -type KeyMap = Map< - string, - { - in: Slot - map?: string - } -> - -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 (config.args) { - buildKeyMap(config.args, map) - } - } - - return map -} - -interface Params { - body: unknown - headers: Record - path: Record - query: Record -} - -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, 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 - ;(params[field.in] as Record)[name] = arg - } else { - params.body = arg - } - } else { - for (const [key, value] of Object.entries(arg ?? {})) { - const field = map.get(key) - - if (field) { - const name = field.map || key - ;(params[field.in] as Record)[name] = value - } else { - const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)) - - if (extra) { - const [prefix, slot] = extra - ;(params[slot] as Record)[key.slice(prefix.length)] = value - } else { - for (const [slot, allowed] of Object.entries(config.allowExtra ?? {})) { - if (allowed) { - ;(params[slot as Slot] as Record)[key] = value - break - } - } - } - } - } - } - } - - stripEmptySlots(params) - - return params -} diff --git a/packages/sdk/js/src/gen/core/params.ts b/packages/sdk/js/src/gen/core/params.ts new file mode 100644 index 000000000..0a09619d1 --- /dev/null +++ b/packages/sdk/js/src/gen/core/params.ts @@ -0,0 +1,142 @@ +type Slot = "body" | "headers" | "path" | "query" + +export type Field = + | { + in: Exclude + /** + * 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 + /** + * Key isn't required for bodies. + */ + key?: string + map?: string + } + +export interface Fields { + allowExtra?: Partial> + args?: ReadonlyArray +} + +export type FieldsConfig = ReadonlyArray + +const extraPrefixesMap: Record = { + $body_: "body", + $headers_: "headers", + $path_: "path", + $query_: "query", +} +const extraPrefixes = Object.entries(extraPrefixesMap) + +type KeyMap = Map< + string, + { + in: Slot + map?: string + } +> + +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 (config.args) { + buildKeyMap(config.args, map) + } + } + + return map +} + +interface Params { + body: unknown + headers: Record + path: Record + query: Record +} + +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, 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 + ;(params[field.in] as Record)[name] = arg + } else { + params.body = arg + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key) + + if (field) { + const name = field.map || key + ;(params[field.in] as Record)[name] = value + } else { + const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)) + + if (extra) { + const [prefix, slot] = extra + ;(params[slot] as Record)[key.slice(prefix.length)] = value + } else { + for (const [slot, allowed] of Object.entries(config.allowExtra ?? {})) { + if (allowed) { + ;(params[slot as Slot] as Record)[key] = value + break + } + } + } + } + } + } + } + + stripEmptySlots(params) + + return params +} diff --git a/packages/sdk/js/src/gen/core/pathSerializer.gen.ts b/packages/sdk/js/src/gen/core/pathSerializer.gen.ts deleted file mode 100644 index 96be3bc5a..000000000 --- a/packages/sdk/js/src/gen/core/pathSerializer.gen.ts +++ /dev/null @@ -1,167 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -interface SerializeOptions extends SerializePrimitiveOptions, SerializerOptions {} - -interface SerializePrimitiveOptions { - allowReserved?: boolean - name: string -} - -export interface SerializerOptions { - /** - * @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 & { - 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 & { - value: Record | 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/gen/core/pathSerializer.ts b/packages/sdk/js/src/gen/core/pathSerializer.ts new file mode 100644 index 000000000..1e27c8d18 --- /dev/null +++ b/packages/sdk/js/src/gen/core/pathSerializer.ts @@ -0,0 +1,165 @@ +interface SerializeOptions extends SerializePrimitiveOptions, SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean + name: string +} + +export interface SerializerOptions { + /** + * @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 & { + 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 & { + value: Record | 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/gen/core/serverSentEvents.gen.ts b/packages/sdk/js/src/gen/core/serverSentEvents.gen.ts deleted file mode 100644 index 8f7fac549..000000000 --- a/packages/sdk/js/src/gen/core/serverSentEvents.gen.ts +++ /dev/null @@ -1,210 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { Config } from "./types.gen.js" - -export type ServerSentEventsOptions = Omit & - Pick & { - /** - * 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) => void - /** - * 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 - url: string - } - -export interface StreamEvent { - data: TData - event?: string - id?: string - retry?: number -} - -export type ServerSentEventsResult = { - stream: AsyncGenerator ? TData[keyof TData] : TData, TReturn, TNext> -} - -export const createSseClient = ({ - onSseError, - onSseEvent, - responseTransformer, - responseValidator, - sseDefaultRetryDelay, - sseMaxRetryAttempts, - sseMaxRetryDelay, - sseSleepFn, - url, - ...options -}: ServerSentEventsOptions): ServerSentEventsResult => { - 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 | undefined) - - if (lastEventId !== undefined) { - headers.set("Last-Event-ID", lastEventId) - } - - try { - const response = await fetch(url, { ...options, headers, signal }) - - 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 = [] - 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/gen/core/types.gen.ts b/packages/sdk/js/src/gen/core/types.gen.ts deleted file mode 100644 index 16408b2d0..000000000 --- a/packages/sdk/js/src/gen/core/types.gen.ts +++ /dev/null @@ -1,91 +0,0 @@ -// 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 interface Client { - /** - * Returns the final request URL. - */ - buildUrl: BuildUrlFn - connect: MethodFn - delete: MethodFn - get: MethodFn - getConfig: () => Config - head: MethodFn - options: MethodFn - patch: MethodFn - post: MethodFn - put: MethodFn - request: RequestFn - setConfig: (config: Config) => Config - trace: MethodFn -} - -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 - /** - * 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 - /** - * The request method. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} - */ - method?: "CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE" - /** - * 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 - /** - * 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 - /** - * 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 -} - -type IsExactlyNeverOrNeverUndefined = [T] extends [never] - ? true - : [T] extends [never | undefined] - ? [undefined] extends [T] - ? false - : true - : false - -export type OmitNever> = { - [K in keyof T as IsExactlyNeverOrNeverUndefined extends true ? never : K]: T[K] -} diff --git a/packages/sdk/js/src/gen/core/types.ts b/packages/sdk/js/src/gen/core/types.ts new file mode 100644 index 000000000..3a12e74c6 --- /dev/null +++ b/packages/sdk/js/src/gen/core/types.ts @@ -0,0 +1,89 @@ +import type { Auth, AuthToken } from "./auth.js" +import type { BodySerializer, QuerySerializer, QuerySerializerOptions } from "./bodySerializer.js" + +export interface Client { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn + connect: MethodFn + delete: MethodFn + get: MethodFn + getConfig: () => Config + head: MethodFn + options: MethodFn + patch: MethodFn + post: MethodFn + put: MethodFn + request: RequestFn + setConfig: (config: Config) => Config + trace: MethodFn +} + +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 + /** + * 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 + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: "CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE" + /** + * 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 + /** + * 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 + /** + * 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 +} + +type IsExactlyNeverOrNeverUndefined = [T] extends [never] + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false + +export type OmitNever> = { + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true ? never : K]: T[K] +} diff --git a/packages/sdk/js/src/gen/core/utils.gen.ts b/packages/sdk/js/src/gen/core/utils.gen.ts deleted file mode 100644 index be18c608a..000000000 --- a/packages/sdk/js/src/gen/core/utils.gen.ts +++ /dev/null @@ -1,109 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { QuerySerializer } from "./bodySerializer.gen.js" -import { - type ArraySeparatorStyle, - serializeArrayParam, - serializeObjectParam, - serializePrimitiveParam, -} from "./pathSerializer.gen.js" - -export interface PathSerializer { - path: Record - 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, - 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 - query?: Record - 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 -} diff --git a/packages/sdk/js/src/gen/sdk.gen.ts b/packages/sdk/js/src/gen/sdk.gen.ts index f900c24f0..b00216b83 100644 --- a/packages/sdk/js/src/gen/sdk.gen.ts +++ b/packages/sdk/js/src/gen/sdk.gen.ts @@ -123,7 +123,7 @@ class Event extends _HeyApiClient { * Get events */ public subscribe(options?: Options) { - return (options?.client ?? this._client).get.sse({ + return (options?.client ?? this._client).get({ url: "/event", ...options, }) -- cgit v1.2.3