UNPKG

@nekofar/warpcast

Version:

TypeScript client for interacting with Warpcast APIs

1,832 lines (1,821 loc) 247 kB
let ofetch = require("ofetch"); let zod = require("zod"); //#region src/client/core/bodySerializer.gen.ts const serializeFormDataPair = (data, key, value) => { 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, key, value) => { if (typeof value === "string") data.append(key, value); else data.append(key, JSON.stringify(value)); }; const formDataBodySerializer = { bodySerializer: (body) => { const data = new FormData(); Object.entries(body).forEach(([key, value]) => { if (value === void 0 || value === null) return; if (Array.isArray(value)) value.forEach((v) => serializeFormDataPair(data, key, v)); else serializeFormDataPair(data, key, value); }); return data; } }; const jsonBodySerializer = { bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value) }; const urlSearchParamsBodySerializer = { bodySerializer: (body) => { const data = new URLSearchParams(); Object.entries(body).forEach(([key, value]) => { if (value === void 0 || value === null) return; if (Array.isArray(value)) value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); else serializeUrlSearchParamsPair(data, key, value); }); return data.toString(); } }; //#endregion //#region src/client/core/params.gen.ts const extraPrefixes = Object.entries({ $body_: "body", $headers_: "headers", $path_: "path", $query_: "query" }); const buildKeyMap = (fields, map) => { if (!map) map = /* @__PURE__ */ 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; }; const stripEmptySlots = (params) => { for (const [slot, value] of Object.entries(params)) if (value && typeof value === "object" && !Object.keys(value).length) delete params[slot]; }; const buildClientParams = (args, fields) => { const params = { body: {}, headers: {}, path: {}, query: {} }; const map = buildKeyMap(fields); let config; 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][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][name] = value; } else params[field.map] = value; else { const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)); if (extra) { const [prefix, slot] = extra; params[slot][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][key] = value; break; } } } } } stripEmptySlots(params); return params; }; //#endregion //#region src/client/core/queryKeySerializer.gen.ts /** * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. */ const queryKeyJsonReplacer = (_key, value) => { if (value === void 0 || typeof value === "function" || typeof value === "symbol") return; 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. */ const stringifyToJsonValue = (input) => { try { const json = JSON.stringify(input, queryKeyJsonReplacer); if (json === void 0) return; return JSON.parse(json); } catch { return; } }; /** * Detects plain objects (including objects with a null prototype). */ const isPlainObject = (value) => { if (value === null || typeof value !== "object") return false; const prototype = Object.getPrototypeOf(value); return prototype === Object.prototype || prototype === null; }; /** * Turns URLSearchParams into a sorted JSON object for deterministic keys. */ const serializeSearchParams = (params) => { const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b)); const result = {}; for (const [key, value] of entries) { const existing = result[key]; if (existing === void 0) { result[key] = value; continue; } if (Array.isArray(existing)) existing.push(value); else result[key] = [existing, value]; } return result; }; /** * Normalizes any accepted value into a JSON-friendly shape for query keys. */ const serializeQueryKeyValue = (value) => { if (value === null) return null; if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value; if (value === void 0 || typeof value === "function" || typeof value === "symbol") return; 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); }; //#endregion //#region src/client/core/serverSentEvents.gen.ts const createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url, ...options }) => { let lastEventId; const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))); const createStream = async function* () { let retryDelay = sseDefaultRetryDelay ?? 3e3; 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); if (lastEventId !== void 0) headers.set("Last-Event-ID", lastEventId); try { const requestInit = { redirect: "follow", ...options, body: options.serializedBody, headers, signal }; let request = new Request(url, requestInit); if (onRequest) request = await onRequest(url, requestInit); const response = await (options.fetch ?? globalThis.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 {} }; 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 = []; let eventName; 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; 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; } } } finally { signal.removeEventListener("abort", abortHandler); reader.releaseLock(); } break; } catch (error) { onSseError?.(error); if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) break; await sleep(Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 3e4)); } } }; return { stream: createStream() }; }; //#endregion //#region src/client/core/pathSerializer.gen.ts const separatorArrayExplode = (style) => { switch (style) { case "label": return "."; case "matrix": return ";"; case "simple": return ","; default: return "&"; } }; const separatorArrayNoExplode = (style) => { switch (style) { case "form": return ","; case "pipeDelimited": return "|"; case "spaceDelimited": return "%20"; default: return ","; } }; const separatorObjectExplode = (style) => { switch (style) { case "label": return "."; case "matrix": return ";"; case "simple": return ","; default: return "&"; } }; const serializeArrayParam = ({ allowReserved, explode, name, style, value }) => { if (!explode) { const joinedValues$1 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style)); switch (style) { case "label": return `.${joinedValues$1}`; case "matrix": return `;${name}=${joinedValues$1}`; case "simple": return joinedValues$1; default: return `${name}=${joinedValues$1}`; } } const separator = separatorArrayExplode(style); const joinedValues = value.map((v) => { if (style === "label" || style === "simple") return allowReserved ? v : encodeURIComponent(v); return serializePrimitiveParam({ allowReserved, name, value: v }); }).join(separator); return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues; }; const serializePrimitiveParam = ({ allowReserved, name, value }) => { if (value === void 0 || 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)}`; }; const serializeObjectParam = ({ allowReserved, explode, name, style, value, valueOnly }) => { if (value instanceof Date) return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; if (style !== "deepObject" && !explode) { let values = []; Object.entries(value).forEach(([key, v]) => { values = [ ...values, key, allowReserved ? v : encodeURIComponent(v) ]; }); const joinedValues$1 = values.join(","); switch (style) { case "form": return `${name}=${joinedValues$1}`; case "label": return `.${joinedValues$1}`; case "matrix": return `;${name}=${joinedValues$1}`; default: return joinedValues$1; } } const separator = separatorObjectExplode(style); const joinedValues = Object.entries(value).map(([key, v]) => serializePrimitiveParam({ allowReserved, name: style === "deepObject" ? `${name}[${key}]` : key, value: v })).join(separator); return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues; }; //#endregion //#region src/client/core/utils.gen.ts const PATH_PARAM_RE = /\{[^{}]+\}/g; const defaultPathSerializer = ({ path, url: _url }) => { 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 = "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 === void 0 || 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, valueOnly: true })); continue; } if (style === "matrix") { url = url.replace(match, `;${serializePrimitiveParam({ name, value })}`); continue; } const replaceValue = encodeURIComponent(style === "label" ? `.${value}` : value); url = url.replace(match, replaceValue); } return url; }; const getUrl = ({ baseUrl, path, query, querySerializer, url: _url }) => { 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; }; function getValidRequestBody(options) { const hasBody = options.body !== void 0; if (hasBody && options.bodySerializer) { if ("serializedBody" in options) return options.serializedBody !== void 0 && options.serializedBody !== "" ? options.serializedBody : null; return options.body !== "" ? options.body : null; } if (hasBody) return options.body; } //#endregion //#region src/client/core/auth.gen.ts const getAuthToken = async (auth, callback) => { 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; }; //#endregion //#region src/client/client/utils.gen.ts const createQuerySerializer = ({ parameters = {}, ...args } = {}) => { const querySerializer = (queryParams) => { const search = []; if (queryParams && typeof queryParams === "object") for (const name in queryParams) { const value = queryParams[name]; if (value === void 0 || value === null) continue; const options = parameters[name] || args; if (Array.isArray(value)) { const serializedArray = serializeArrayParam({ allowReserved: options.allowReserved, explode: true, name, style: "form", value, ...options.array }); if (serializedArray) search.push(serializedArray); } else if (typeof value === "object") { const serializedObject = serializeObjectParam({ allowReserved: options.allowReserved, explode: true, name, style: "deepObject", value, ...options.object }); if (serializedObject) search.push(serializedObject); } else { const serializedPrimitive = serializePrimitiveParam({ allowReserved: options.allowReserved, name, value }); if (serializedPrimitive) search.push(serializedPrimitive); } } return search.join("&"); }; return querySerializer; }; /** * Infers parseAs value from provided Content-Type header. */ const getParseAs = (contentType) => { if (!contentType) 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"; }; /** * Map our parseAs value to ofetch responseType when not explicitly provided. */ const mapParseAsToResponseType = (parseAs, explicit) => { if (explicit) return explicit; switch (parseAs) { case "arrayBuffer": case "blob": case "json": case "text": case "stream": return parseAs; case "formData": case "auto": default: return; } }; const checkForExistence = (options, name) => { if (!name) return false; if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) return true; return false; }; const setAuthParams = async ({ security, ...options }) => { 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; } } }; const buildUrl = (options) => getUrl({ baseUrl: options.baseUrl, path: options.path, query: options.query, querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer), url: options.url }); const mergeConfigs = (a, b) => { 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; }; const headersEntries = (headers) => { const entries = []; headers.forEach((value, key) => { entries.push([key, value]); }); return entries; }; const mergeHeaders = (...headers) => { const mergedHeaders = new Headers(); for (const header of headers) { if (!header) continue; const iterator = header instanceof Headers ? headersEntries(header) : 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); else if (value !== void 0) mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : value); } return mergedHeaders; }; /** * Heuristic to detect whether a request body can be safely retried. */ const isRepeatableBody = (body) => { if (body == null) return true; if (typeof body === "string") return true; if (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) return true; if (typeof Uint8Array !== "undefined" && body instanceof Uint8Array) return true; if (typeof ArrayBuffer !== "undefined" && body instanceof ArrayBuffer) return true; if (typeof Blob !== "undefined" && body instanceof Blob) return true; if (typeof FormData !== "undefined" && body instanceof FormData) return true; if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream) return false; return false; }; /** * Small helper to unify data vs fields return style. */ const wrapDataReturn = (data, result, responseStyle) => (responseStyle ?? "fields") === "data" ? data : { data, ...result }; /** * Small helper to unify error vs fields return style. */ const wrapErrorReturn = (error, result, responseStyle) => (responseStyle ?? "fields") === "data" ? void 0 : { error, ...result }; /** * Build options for $ofetch.raw from our resolved opts and body. */ const buildOfetchOptions = (opts, body, responseType, retryOverride) => ({ agent: opts.agent, body, credentials: opts.credentials, dispatcher: opts.dispatcher, headers: opts.headers, ignoreResponseError: opts.ignoreResponseError ?? true, method: opts.method, onRequest: opts.onRequest, onRequestError: opts.onRequestError, onResponse: opts.onResponse, onResponseError: opts.onResponseError, parseResponse: opts.parseResponse, query: void 0, responseType, retry: retryOverride ?? opts.retry, retryDelay: opts.retryDelay, retryStatusCodes: opts.retryStatusCodes, signal: opts.signal, timeout: opts.timeout }); /** * Parse a successful response, handling empty bodies and stream cases. */ const parseSuccess = async (response, opts, ofetchResponseType) => { if (ofetchResponseType === "stream") return response.body; const inferredParseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json"; if (response.status === 204 || response.headers.get("Content-Length") === "0") switch (inferredParseAs) { case "arrayBuffer": case "blob": case "text": return await response[inferredParseAs](); case "formData": return new FormData(); case "stream": return response.body; default: return {}; } let data = response._data; if (inferredParseAs === "formData" || typeof data === "undefined") switch (inferredParseAs) { case "arrayBuffer": case "blob": case "formData": case "text": data = await response[inferredParseAs](); break; case "json": if (!await response.clone().text()) data = {}; else data = await response.json(); break; case "stream": return response.body; } if (inferredParseAs === "json") { if (opts.responseValidator) await opts.responseValidator(data); if (opts.responseTransformer) data = await opts.responseTransformer(data); } return data; }; /** * Parse an error response payload. */ const parseError = async (response) => { let error = response._data; if (typeof error === "undefined") { const textError = await response.text(); try { error = JSON.parse(textError); } catch { error = textError; } } return error ?? {}; }; var Interceptors = class { constructor() { this.fns = []; } clear() { this.fns = []; } eject(id) { const index = this.getInterceptorIndex(id); if (this.fns[index]) this.fns[index] = null; } exists(id) { const index = this.getInterceptorIndex(id); return Boolean(this.fns[index]); } getInterceptorIndex(id) { if (typeof id === "number") return this.fns[id] ? id : -1; return this.fns.indexOf(id); } update(id, fn) { const index = this.getInterceptorIndex(id); if (this.fns[index]) { this.fns[index] = fn; return id; } return false; } use(fn) { this.fns.push(fn); return this.fns.length - 1; } }; 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" }; const createConfig = (override = {}) => ({ ...jsonBodySerializer, headers: defaultHeaders, ignoreResponseError: true, parseAs: "auto", querySerializer: defaultQuerySerializer, ...override }); //#endregion //#region src/client/client/client.gen.ts const createClient = (config = {}) => { let _config = mergeConfigs(createConfig(), config); const getConfig = () => ({ ..._config }); const setConfig = (config$1) => { _config = mergeConfigs(_config, config$1); return getConfig(); }; const interceptors = createInterceptors(); const resolveOptions = async (options) => { const opts = { ..._config, ...options, headers: mergeHeaders(_config.headers, options.headers), serializedBody: void 0 }; if (opts.security) await setAuthParams({ ...opts, security: opts.security }); if (opts.requestValidator) await opts.requestValidator(opts); if (opts.body !== void 0 && opts.bodySerializer) opts.serializedBody = opts.bodySerializer(opts.body); if (opts.body === void 0 || opts.serializedBody === "") opts.headers.delete("Content-Type"); if (opts.body !== void 0 && opts.bodySerializer === null && (opts.headers.get("Content-Type") || "").toLowerCase() === "application/json") { const b = opts.body; if (typeof FormData !== "undefined" && b instanceof FormData) opts.headers.delete("Content-Type"); else if (typeof URLSearchParams !== "undefined" && b instanceof URLSearchParams) opts.headers.set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); else if (typeof Blob !== "undefined" && b instanceof Blob) { const t = b.type?.trim(); if (t) opts.headers.set("Content-Type", t); else opts.headers.delete("Content-Type"); } } return { networkBody: getValidRequestBody(opts), opts, url: buildUrl(opts) }; }; const applyRequestInterceptors = async (request$1, opts, body) => { for (const fn of interceptors.request.fns) if (fn) request$1 = await fn(request$1, opts); opts.headers = request$1.headers; opts.method = request$1.method; opts.signal = request$1.signal; if (typeof FormData !== "undefined" && body instanceof FormData) opts.headers.delete("Content-Type"); return request$1; }; const buildNetworkOptions = (opts, body, responseType) => { return buildOfetchOptions(opts, body, responseType, isRepeatableBody(body) ? opts.retry : 0); }; const request = async (options) => { const { networkBody: initialNetworkBody, opts, url } = await resolveOptions(options); const ofetchResponseType = mapParseAsToResponseType(opts.parseAs, opts.responseType); const $ofetch = opts.ofetch ?? ofetch.ofetch; const networkBody = initialNetworkBody; const requestInit = { body: networkBody, headers: opts.headers, method: opts.method, redirect: "follow", signal: opts.signal }; let request$1 = new Request(url, requestInit); request$1 = await applyRequestInterceptors(request$1, opts, networkBody); const finalUrl = request$1.url; const responseOptions = buildNetworkOptions(opts, networkBody, ofetchResponseType); let response = await $ofetch.raw(finalUrl, responseOptions); for (const fn of interceptors.response.fns) if (fn) response = await fn(response, request$1, opts); const result = { request: request$1, response }; if (response.ok) return wrapDataReturn(await parseSuccess(response, opts, ofetchResponseType), result, opts.responseStyle); let finalError = await parseError(response); for (const fn of interceptors.error.fns) if (fn) finalError = await fn(finalError, response, request$1, opts); finalError = finalError || {}; if (opts.throwOnError) throw finalError; return wrapErrorReturn(finalError, result, opts.responseStyle); }; const makeMethodFn = (method) => (options) => request({ ...options, method }); const makeSseFn = (method) => async (options) => { const { networkBody, opts, url } = await resolveOptions(options); const optsForSse = { ...opts }; delete optsForSse.body; return createSseClient({ ...optsForSse, fetch: opts.fetch, headers: opts.headers, method, onRequest: async (url$1, init) => { let request$1 = new Request(url$1, init); request$1 = await applyRequestInterceptors(request$1, opts, networkBody); return request$1; }, serializedBody: networkBody, signal: opts.signal, url }); }; return { buildUrl, connect: makeMethodFn("CONNECT"), delete: makeMethodFn("DELETE"), get: makeMethodFn("GET"), getConfig, head: makeMethodFn("HEAD"), interceptors, options: makeMethodFn("OPTIONS"), patch: makeMethodFn("PATCH"), post: makeMethodFn("POST"), put: makeMethodFn("PUT"), request, setConfig, sse: { connect: makeSseFn("CONNECT"), delete: makeSseFn("DELETE"), get: makeSseFn("GET"), head: makeSseFn("HEAD"), options: makeSseFn("OPTIONS"), patch: makeSseFn("PATCH"), post: makeSseFn("POST"), put: makeSseFn("PUT"), trace: makeSseFn("TRACE") }, trace: makeMethodFn("TRACE") }; }; //#endregion //#region src/client/client.gen.ts const client = createClient(createConfig({ baseUrl: "https://api.farcaster.xyz" })); //#endregion //#region src/client/schemas.gen.ts const ProfilePictureSchema = { type: "object", properties: { url: { type: "string", format: "uri" }, verified: { type: "boolean" } } }; const BioSchema = { type: "object", properties: { text: { type: "string" }, mentions: { type: "array", items: {} }, channelMentions: { type: "array", items: {} } } }; const LocationSchema = { type: "object", properties: { placeId: { type: "string" }, description: { type: "string" } } }; const ProfileSchema = { type: "object", properties: { bio: { $ref: "#/components/schemas/Bio" }, location: { $ref: "#/components/schemas/Location" } } }; const ViewerContextSchema = { type: "object", properties: { following: { type: "boolean" }, followedBy: { type: "boolean" }, enableNotifications: { type: "boolean" }, canSendDirectCasts: { type: "boolean" }, hasUploadedInboxKeys: { type: "boolean" } } }; const UserSchema = { type: "object", required: [ "fid", "displayName", "username" ], properties: { fid: { type: "integer" }, username: { type: "string" }, displayName: { type: "string" }, pfp: { $ref: "#/components/schemas/ProfilePicture" }, profile: { $ref: "#/components/schemas/Profile" }, followerCount: { type: "integer" }, followingCount: { type: "integer" }, viewerContext: { $ref: "#/components/schemas/ViewerContext" } } }; const OnboardingStateSchema = { type: "object", properties: { id: { type: "string", format: "uuid" }, email: { type: "string", format: "email" }, user: { $ref: "#/components/schemas/User" }, hasOnboarding: { type: "boolean" }, hasConfirmedEmail: { type: "boolean" }, handledConnectAddress: { type: "boolean" }, canRegisterUsername: { type: "boolean" }, needsRegistrationPayment: { type: "boolean" }, hasFid: { type: "boolean" }, hasFname: { type: "boolean" }, hasDelegatedSigner: { type: "boolean" }, hasSetupProfile: { type: "boolean" }, hasCompletedRegistration: { type: "boolean" }, hasStorage: { type: "boolean" }, handledPushNotificationsNudge: { type: "boolean" }, handledContactsNudge: { type: "boolean" }, handledInterestsNudge: { type: "boolean" }, hasValidPaidInvite: { type: "boolean" }, hasWarpcastWalletAddress: { type: "boolean" }, hasPhone: { type: "boolean" }, needsPhone: { type: "boolean" }, sponsoredRegisterEligible: { type: "boolean" }, geoRestricted: { type: "boolean" } } }; const OnboardingStateResponseSchema = { type: "object", properties: { result: { type: "object", properties: { state: { $ref: "#/components/schemas/OnboardingState" } } } } }; const ErrorResponseSchema = { type: "object", properties: { errors: { type: "array", items: { type: "object", properties: { message: { type: "string", description: "Error message describing the issue" } } } } } }; const UserWithExtrasSchema = { allOf: [{ $ref: "#/components/schemas/User" }, { type: "object", properties: { connectedAccounts: { type: "array", items: {} } } }] }; const UserExtrasSchema = { type: "object", properties: { fid: { type: "integer" }, custodyAddress: { type: "string" }, ethWallets: { type: "array", items: { type: "string" } }, solanaWallets: { type: "array", items: { type: "string" } }, walletLabels: { type: "array", items: { type: "object", properties: { address: { type: "string" }, labels: { type: "array", items: { type: "string" } } } } }, v2: { type: "boolean" }, publicSpamLabel: { type: "string" } } }; const UserByFidResponseSchema = { type: "object", properties: { result: { type: "object", properties: { user: { $ref: "#/components/schemas/UserWithExtras" }, collectionsOwned: { type: "array", items: {} }, extras: { $ref: "#/components/schemas/UserExtras" } } } } }; const ValidationErrorSchema = { type: "object", description: "Represents a single validation error", properties: { instancePath: { type: "string", description: "JSON Pointer to the part of the request that failed validation", example: "/fid" }, schemaPath: { type: "string", description: "JSON Schema path that was violated", example: "ApiFid/type" }, keyword: { type: "string", description: "The JSON Schema keyword that failed", example: "type" }, params: { type: "object", description: "Additional parameters describing the validation error", additionalProperties: true, example: { type: "integer" } }, message: { type: "string", description: "Human-readable error description", example: "must be integer" } }, required: [ "instancePath", "schemaPath", "keyword", "message" ] }; const BadRequestErrorSchema = { type: "object", description: "Standard 400 Bad Request error response", properties: { errors: { type: "array", description: "Array of validation errors", items: { $ref: "#/components/schemas/ValidationError" } } }, required: ["errors"] }; const DirectCastMessageReactionSchema = { type: "object", required: ["reaction", "count"], properties: { reaction: { type: "string", description: "Emoji used for the reaction", example: "🔥" }, count: { type: "integer", minimum: 1, description: "Number of users who reacted with this emoji", example: 3 }, emoji: { type: "string", description: "Emoji used for the reaction (legacy field)" }, userFids: { type: "array", items: { type: "integer" }, description: "List of Farcaster IDs who reacted" } } }; const DirectCastMessageViewerContextSchema = { type: "object", properties: { isLastReadMessage: { type: "boolean", description: "Whether this is the last read message", example: false }, focused: { type: "boolean", description: "Whether the message is focused", example: false }, reactions: { type: "array", items: { type: "string" }, description: "User's reactions to this message" } } }; const DirectCastMessageSchema = { type: "object", required: [ "conversationId", "senderFid", "messageId", "serverTimestamp", "type", "message", "hasMention", "reactions", "isPinned", "isDeleted", "senderContext" ], properties: { conversationId: { type: "string", description: "ID of the conversation this message belongs to" }, senderFid: { type: "integer", description: "Farcaster ID of the message sender" }, messageId: { type: "string", description: "Unique identifier for the message" }, serverTimestamp: { type: "integer", format: "int64", description: "Server timestamp when message was sent (Unix milliseconds)", example: 1753112479748 }, type: { type: "string", enum: [ "text", "image", "reaction", "link", "group_membership_addition", "pin_message", "message_ttl_change" ], description: "Type of the message", example: "text" }, message: { type: "string", description: "Content of the message" }, hasMention: { type: "boolean", description: "Whether the message contains mentions", example: false }, reactions: { type: "array", items: { $ref: "#/components/schemas/DirectCastMessageReaction" }, description: "List of reactions to the message" }, isPinned: { type: "boolean", description: "Whether the message is pinned", example: false }, isDeleted: { type: "boolean", description: "Whether the message is deleted", example: false }, senderContext: { $ref: "#/components/schemas/User" }, viewerContext: { $ref: "#/components/schemas/DirectCastMessageViewerContext" }, inReplyTo: { $ref: "#/components/schemas/DirectCastMessage" }, metadata: { $ref: "#/components/schemas/DirectCastMessageMetadata" }, actionTargetUserContext: { $ref: "#/components/schemas/User" }, isProgrammatic: { type: "boolean", description: "Whether the message was sent programmatically", example: false }, mentions: { type: "array", items: { $ref: "#/components/schemas/DirectCastMessageMention" }, description: "List of mentions in the message" } } }; const DirectCastMessageMetadataSchema = { type: "object", properties: { casts: { type: "array", items: { type: "object", additionalProperties: true }, description: "Cast metadata if message contains cast references" }, urls: { type: "array", items: { type: "object", additionalProperties: true }, description: "URL metadata if message contains links" }, medias: { type: "array", items: { type: "object", additionalProperties: true }, description: "Media metadata if message contains media" } } }; const DirectCastMessageMentionSchema = { type: "object", required: [ "user", "textIndex", "length" ], properties: { user: { $ref: "#/components/schemas/User" }, textIndex: { type: "integer", description: "Starting index of the mention in the message text", example: 19 }, length: { type: "integer", description: "Length of the mention text", example: 8 } } }; const DirectCastConversationViewerContextSchema = { type: "object", properties: { access: { type: "string", enum: ["read-write", "read-only"], description: "Access level for the conversation", example: "read-write" }, category: { type: "string", description: "Category of the conversation", example: "default" }, archived: { type: "boolean", description: "Whether the conversation is archived", example: false }, lastReadAt: { type: "integer", format: "int64", description: "Timestamp of last read (Unix milliseconds)", example: 1753650746109 }, muted: { type: "boolean", description: "Whether the conversation is muted", example: false }, manuallyMarkedUnread: { type: "boolean", description: "Whether the conversation is manually marked as unread", example: false }, pinned: { type: "boolean", description: "Whether the conversation is pinned", example: false }, unreadCount: { type: "integer", minimum: 0, description: "Number of unread messages", example: 0 }, unreadMentionsCount: { type: "integer", minimum: 0, description: "Number of unread mentions", example: 0 }, counterParty: { $ref: "#/components/schemas/User", description: "The other participant in a 1:1 conversation" }, tag: { type: "string", description: "Tag associated with the conversation", example: "automated" } } }; const DirectCastConversationSchema = { type: "object", required: [ "conversationId", "isGroup", "createdAt", "viewerContext", "adminFids", "lastReadTime" ], properties: { conversationId: { type: "string", description: "Unique identifier for the conversation" }, name: { type: "string", description: "Name of the conversation (for group conversations)" }, description: { type: "string", description: "Description of the conversation" }, photoUrl: { type: "string", format: "uri", description: "URL of the conversation photo" }, adminFids: { type: "array", items: { type: "integer" }, description: "List of admin Farcaster IDs" }, removedFids: { type: "array", items: { type: "integer" }, description: "List of removed Farcaster IDs" }, participants: { type: "array", items: { $ref: "#/components/schemas/User" }, description: "List of conversation participants" }, lastReadTime: { type: "integer", format: "int64", description: "Timestamp of last read time (Unix milliseconds)", example: 1741871452933 }, selfLastReadTime: { type: "integer", format: "int64", description: "Timestamp of viewer's last read time (Unix milliseconds)", example: 1753650746109 }, pinnedMessages: { type: "array", items: { $ref: "#/components/schemas/DirectCastMessage" }, description: "List of pinned messages in the conversation" }, hasPinnedMessages: { type: "boolean", description: "Whether the conversation has pinned messages", example: false }, isGroup: { type: "boolean", description: "Whether this is a group conversation", example: true }, isCollectionTokenGated: { type: "boolean", description: "Whether the conversation is collection token gated", example: false }, activeParticipantsCount: { type: "integer", minimum: 0, description: "Number of active participants in the conversation", example: 2 }, messageTTLDays: { oneOf: [{ type: "integer", minimum: 0, description: "Number of days until message expires" }, { type: "string", enum: ["Infinity"], description: "Messages never expire" }], description: "Message time-to-live in days, or \"Infinity\" for no expiration", examples: [365, "Infinity"] }, createdAt: { type: "integer", format: "int64", description: "Timestamp when conversation was created (Unix milliseconds)", example: 1709952982363 }, unreadCount: { type: "integer", minimum: 0, description: "Number of unread messages", example: 0 }, muted: { type: "boolean", description: "Whether the conversation is muted", example: false }, hasMention: { type: "boolean", description: "Whether the conversation has mentions", example: false }, lastMessage: { $ref: "#/components/schemas/DirectCastMessage" }, viewerContext: { $ref: "#/components/schemas/DirectCastConversationViewerContext" } } }; const DirectCastInboxResultSchema = { type: "object", required: [ "hasArchived", "hasUnreadRequests", "requestsCount", "conversations" ], properties: { hasArchived: { type: "boolean", description: "Whether user has archived conversations", example: false }, hasUnreadRequests: { type: "boolean", description: "Whether user has unread conversation requests", example: false }, requestsCount: { type: "integer", minimum: 0, description: "Total number of conversation requests", example: 12 }, conversations: { type: "array", items: { $ref: "#/components/schemas/DirectCastConversation" } } } }; const PaginationCursorSchema = { type: "object", properties: { cursor: { type: "string", description: "Base64 encoded cursor for pagination" } }, additionalProperties: true }; const DirectCastInboxResponseSchema = { type: "object", required: ["result"], properties: { result: { $ref: "#/components/schemas/DirectCastInboxResult" }, next: { $ref: "#/components/schemas/PaginationCursor" } } }; const CastActionSchema = { type: "object", properties: { id: { type: "string" }, name: { type: "string" }, octicon: { type: "string" }, actionUrl: { type: "string" }, action: { type: "object", properties: { actionType: { type: "string" }, postUrl: { type: "string" } } } } }; const UserAppContextResponseSchema = { type: "object", properties: { result: { type: "object", properties: { context: { type: "object", properties: { canAddLinks: { type: "boolean" }, showConnectedApps: { type: "boolean" }, signerRequestsEnabled: { type: "boolean" }, prompts: { type: "array", items: {} }, adminForChannelKeys: { type: "array", items: { type: "string" } }, modOfChannelKeys: { type: "array", items: { type: "string" } }, memberOfChannelKeys: { type: "array", items: { type: "string" } }, canEditAllChannels: { type: "boolean" }, canUploadVideo: { type: "boolean" }, statsigEnabled: { type: "boolean" }, shouldPromptForPushNotifications: { type: "boolean" }, shouldPromptForUserFollowsSyncContacts: { type: "boolean" }, castActions: { type: "array", items: { $ref: "#/components/schemas/CastAction" } }, canAddCastAction: { type: "boolean" }, enabledCastAction: { $ref: "#/components/schemas/CastAction" }, notificationTabsV2: { type: "array", items: { type: "object", properties: { id: { type: "string" }, name: { type: "string" } } } }, enabledVideoAutoplay: { type: "boolean" }, regularCastByteLimit: { type: "integer" }, longCastByteLimit: { type: "integer" }, newUserStatus: { type: "object" }, country: { type: "string" }, higherClientEventSamplingRateEnabled: { type: "boolean" } } } } } } }; const UserPreferencesResponseSchema = { type: "object", properties: { result: { type: "object", properties: { preferences: { type: "object", additionalProperties: true } } } } }; const ChannelSchema = { type: "object", properties: { type: { type: "string" }, key: { type: "string" }, name: { type: "string" }, imageUrl: { type: "string" }, fastImageUrl: { type: "string" }, feeds: { type: "array", items: { type: "object", properties: { name: { type: "string" }, type: { type: "string" } } } }, description: { type: "string" }, followerCount: { type: "integer" }, memberCount: { type: "integer" }, showCastSourceLabels: { type: "boolean" }, showCastTags: { type: "boolean" }, sectionRank: { type: "integer" }, subscribable: { type: "boolean" }, publicCasting: { type: "boolean" }, inviteCode: { type: "string" }, headerImageUrl: { type: "string" }, headerAction: { type: "object", properties: { title: { type: "string" }, target: { type: "string" } } }, headerActionMetadata: { type: "object", additionalProperties: true }, viewerContext: { type: "object", properties: { following: { type: "boolean" }, isMember: { type: "boolean" }, hasUnseenItems: { type: "boolean" }, favoritePosition: { type: "integer" }, activityRank: { type: "integer" }, canCast: { type: "boolean" } } } } }; const HighlightedChannelsResponseSchema = { type: "object", properties: { result: { type: "object", properties: { channels: { type: "array", items: { $ref: "#/components/schemas/Channel" } }, viewerContext: { type: "object", properties: { defaultFeed: { type: "string" } } } } } } }; const ImageEmbedSchema = { type: "object", properties: { type: { type: "string", enum: ["image"] }, url: { type: "string" }, sourceUrl: { type: "string" }, media: { type: "object", properties: { version: { type: "string" }, width: { type: "integer" }, height: { type: "integer" }, staticRaster: { type: "string" }, mimeType: { type: "string" } } }, alt: { type: "string" } } }; const UrlEmbedSchema = { type: "object", required: ["type", "openGraph"], properties: { type: { type: "string", enum: ["url"] }, openGraph: { type: "object", required: ["url"], properties: { url: { type: "string" }, sourceUrl: { type: "string" }, title: { type: "string" }, description: { type: "string" }, domain: { type: "string" }, image: { type: "string" }, useLargeImage: { type: "boolean" } } } } }; const VideoEmbedSchema = { type: "object", properties: { type: { type: "string", enum: ["video"] } } }; const RecasterSchema = { type: "object", properties: { fid: { type: "integer" }, username: { type: "string" }, displayName: { type: "string" }, recastHash: { type: "string" } } }; const CastSchema = { type: "object", required: [ "hash", "author", "text", "timestamp", "replies", "reactions", "recasts", "watches" ], properties: { hash: { type: "string", description: "Unique hash identifier for the cast" }, threadHash: { type: "string", description: "Hash identifier for the thread this cast belongs to" }, parentHash: { type: "string", description: "Hash identifier of the parent cast (if this is a reply)" }, parentSource: { type: "object", properties: { type: { type: "string", enum: ["url"] }, url: { type: "string" } } }, author: { $ref: "#/components/schemas/User" }, text: { type: "string", description: "The text content of the cast" }, timestamp: { type: "integer", format: "int64", description: "Unix timestamp in milliseconds" }, mentions: { type: "array", items: { $ref: "#/components/schemas/User" } }, embeds: { type: "object", properties: { images: { type: "array", items: { $ref: "#/components/schemas/ImageEmbed" } }, urls: { type: "array", items: { $ref: "#/components/schemas/UrlEmbed" } }, videos: { type: "array", items: { $ref: "#/components/schemas/VideoEmbed" } }, unknowns: { type: "array", items: { type: "object" } }, processedCastText: { type: "string" }, groupInvites: { type: "array", items: { type: "object" } } } }, replies: { type: "object", required: ["count"], properties: { count: { type: "integer" } } }, reactions: { type: "object", required: ["count"], properties: { count: { type: "integer" } } }, recasts: { type: "object", required: ["count"], properties: { count: { type: "integer" }, recasters: { type: "array", items: { $ref: "#/components/schemas/Recaster" } } } }, watches: { type: "object", required: ["count"], properties: { count: { type: "integer" } } }, recast: { type: "boolean" }, tags: { type: "array", items: { type: "object", properties: { type: { type: "string" }, id: { type: "string" }, name: { type: "string" }, imageUrl: { type: "string" } } } }, quoteCount: { type: "integer" }, combinedRecastCount: { type: "integer" }, channel: { type: "object", properties: { key: { type: "string" }, name: { type: "string" }, imageUrl: { type: "string" }, authorContext: { type: "object", properties: { role: { type: "string" }, restricted: { type: "boolean" }, banned: { type: "boolean" } } }, authorRole: { type: "string" } } }, viewerContext: { type: "object", properties: { reacted: { type: "boolean" }, recast: { type: "boolean" }, bookmarked: { type: "boolean" }