@nekofar/warpcast
Version:
TypeScript client for interacting with Warpcast APIs
1,832 lines (1,821 loc) • 247 kB
JavaScript
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" }