mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-07 11:02:12 +02:00
chore: improve upon mcp prompts (#494)
* chore: improve upon mcp prompts * Update api/mcp_server/instructions.py Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
parent
88f4477edb
commit
79a4a3c9f1
39 changed files with 3890 additions and 4744 deletions
|
|
@ -49,12 +49,16 @@ export default withSentryConfig(nextConfig, {
|
|||
// side errors will fail.
|
||||
tunnelRoute: "/monitoring",
|
||||
|
||||
// Automatically tree-shake Sentry logger statements to reduce bundle size
|
||||
disableLogger: true,
|
||||
webpack: {
|
||||
// Automatically tree-shake Sentry logger statements to reduce bundle size
|
||||
treeshake: {
|
||||
removeDebugLogging: true,
|
||||
},
|
||||
|
||||
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
|
||||
// See the following for more information:
|
||||
// https://docs.sentry.io/product/crons/
|
||||
// https://vercel.com/docs/cron-jobs
|
||||
automaticVercelMonitors: true,
|
||||
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
|
||||
// See the following for more information:
|
||||
// https://docs.sentry.io/product/crons/
|
||||
// https://vercel.com/docs/cron-jobs
|
||||
automaticVercelMonitors: true,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,22 @@
|
|||
import { defineConfig } from '@hey-api/openapi-ts';
|
||||
import { loadEnvConfig } from '@next/env';
|
||||
|
||||
// Load .env.local / .env the same way Next.js does, so client generation targets
|
||||
// the backend THIS worktree actually runs on (per-worktree BACKEND_URL set by
|
||||
// scripts/worktree-assign-port.sh). Falls back to the default dev port if unset.
|
||||
loadEnvConfig(process.cwd());
|
||||
|
||||
const backendUrl = (
|
||||
process.env.BACKEND_URL ||
|
||||
process.env.NEXT_PUBLIC_BACKEND_URL ||
|
||||
'http://127.0.0.1:8000'
|
||||
).replace(/\/+$/, '');
|
||||
|
||||
export default defineConfig({
|
||||
input: 'http://127.0.0.1:8000/api/v1/openapi.json',
|
||||
input: `${backendUrl}/api/v1/openapi.json`,
|
||||
output: 'src/client',
|
||||
plugins: [{
|
||||
name: '@hey-api/client-fetch',
|
||||
runtimeConfigPath: '../lib/apiClient',
|
||||
runtimeConfigPath: './src/lib/apiClient',
|
||||
}],
|
||||
});
|
||||
|
|
|
|||
6878
ui/package-lock.json
generated
6878
ui/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -30,7 +30,7 @@
|
|||
"@radix-ui/react-switch": "^1.1.4",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@sentry/nextjs": "^9.28.1",
|
||||
"@sentry/nextjs": "^10.63.0",
|
||||
"@stackframe/stack": "^2.8.80",
|
||||
"@xyflow/react": "^12.10.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@hey-api/openapi-ts": "^0.95.0",
|
||||
"@hey-api/openapi-ts": "^0.99.0",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
|
|
|
|||
|
|
@ -679,11 +679,11 @@ export const useWebSocketRTC = ({ workflowId, workflowRunId, accessToken, initia
|
|||
if (turnResponse.data) {
|
||||
turnCredentialsRef.current = turnResponse.data;
|
||||
logger.info(`TURN credentials obtained, TTL: ${turnResponse.data.ttl}s`);
|
||||
} else if (turnResponse.response.status === 503) {
|
||||
} else if (turnResponse.response?.status === 503) {
|
||||
// TURN not configured on server - this is OK, we'll use STUN only
|
||||
logger.info('TURN server not configured, using STUN only');
|
||||
} else {
|
||||
logger.warn(`Failed to fetch TURN credentials: ${turnResponse.response.status}`);
|
||||
logger.warn(`Failed to fetch TURN credentials: ${turnResponse.response?.status}`);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn('Failed to fetch TURN credentials, continuing without TURN:', e);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { createClientConfig } from '../lib/apiClient';
|
||||
|
||||
import { type ClientOptions, type Config, createClient, createConfig } from './client';
|
||||
import { type Client, type ClientOptions, type Config, createClient, createConfig } from './client';
|
||||
import type { ClientOptions as ClientOptions2 } from './types.gen';
|
||||
|
||||
/**
|
||||
|
|
@ -15,4 +15,4 @@ import type { ClientOptions as ClientOptions2 } from './types.gen';
|
|||
*/
|
||||
export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T>;
|
||||
|
||||
export const client = createClient(createClientConfig(createConfig<ClientOptions2>({ baseUrl: 'https://app.dograh.com' })));
|
||||
export const client: Client = createClient(createClientConfig(createConfig<ClientOptions2>({ baseUrl: 'https://app.dograh.com' })));
|
||||
|
|
|
|||
|
|
@ -48,10 +48,7 @@ export const createClient = (config: Config = {}): Client => {
|
|||
};
|
||||
|
||||
if (opts.security) {
|
||||
await setAuthParams({
|
||||
...opts,
|
||||
security: opts.security,
|
||||
});
|
||||
await setAuthParams(opts);
|
||||
}
|
||||
|
||||
if (opts.requestValidator) {
|
||||
|
|
@ -75,171 +72,154 @@ export const createClient = (config: Config = {}): Client => {
|
|||
};
|
||||
|
||||
const request: Client['request'] = async (options) => {
|
||||
const { opts, url } = await beforeRequest(options);
|
||||
const requestInit: ReqInit = {
|
||||
redirect: 'follow',
|
||||
...opts,
|
||||
body: getValidRequestBody(opts),
|
||||
};
|
||||
const throwOnError = options.throwOnError ?? _config.throwOnError;
|
||||
const responseStyle = options.responseStyle ?? _config.responseStyle;
|
||||
|
||||
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: Response;
|
||||
let request: Request | undefined;
|
||||
let response: Response | undefined;
|
||||
|
||||
try {
|
||||
response = await _fetch(request);
|
||||
} catch (error) {
|
||||
// Handle fetch exceptions (AbortError, network errors, etc.)
|
||||
let finalError = error;
|
||||
const { opts, url } = await beforeRequest(options);
|
||||
const requestInit: ReqInit = {
|
||||
redirect: 'follow',
|
||||
...opts,
|
||||
body: getValidRequestBody(opts),
|
||||
};
|
||||
|
||||
for (const fn of interceptors.error.fns) {
|
||||
request = new Request(url, requestInit);
|
||||
|
||||
for (const fn of interceptors.request.fns) {
|
||||
if (fn) {
|
||||
finalError = (await fn(error, undefined as any, request, opts)) as unknown;
|
||||
request = await fn(request, opts);
|
||||
}
|
||||
}
|
||||
|
||||
finalError = finalError || ({} as unknown);
|
||||
// fetch must be assigned here, otherwise it would throw the error:
|
||||
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
|
||||
const _fetch = opts.fetch!;
|
||||
|
||||
if (opts.throwOnError) {
|
||||
throw finalError;
|
||||
response = await _fetch(request);
|
||||
|
||||
for (const fn of interceptors.response.fns) {
|
||||
if (fn) {
|
||||
response = await fn(response, request, opts);
|
||||
}
|
||||
}
|
||||
|
||||
// Return error response
|
||||
return opts.responseStyle === 'data'
|
||||
? undefined
|
||||
: {
|
||||
error: finalError,
|
||||
request,
|
||||
response: undefined as any,
|
||||
};
|
||||
}
|
||||
const result = {
|
||||
request,
|
||||
response,
|
||||
};
|
||||
|
||||
for (const fn of interceptors.response.fns) {
|
||||
if (fn) {
|
||||
response = await fn(response, request, opts);
|
||||
}
|
||||
}
|
||||
if (response.ok) {
|
||||
const parseAs =
|
||||
(opts.parseAs === 'auto'
|
||||
? getParseAs(response.headers.get('Content-Type'))
|
||||
: opts.parseAs) ?? 'json';
|
||||
|
||||
const result = {
|
||||
request,
|
||||
response,
|
||||
};
|
||||
if (response.status === 204 || response.headers.get('Content-Length') === '0') {
|
||||
let emptyData: any;
|
||||
switch (parseAs) {
|
||||
case 'arrayBuffer':
|
||||
case 'blob':
|
||||
case 'text':
|
||||
emptyData = await response[parseAs]();
|
||||
break;
|
||||
case 'formData':
|
||||
emptyData = new FormData();
|
||||
break;
|
||||
case 'stream':
|
||||
emptyData = response.body;
|
||||
break;
|
||||
case 'json':
|
||||
default:
|
||||
emptyData = {};
|
||||
break;
|
||||
}
|
||||
return opts.responseStyle === 'data'
|
||||
? emptyData
|
||||
: {
|
||||
data: emptyData,
|
||||
...result,
|
||||
};
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
const parseAs =
|
||||
(opts.parseAs === 'auto'
|
||||
? getParseAs(response.headers.get('Content-Type'))
|
||||
: opts.parseAs) ?? 'json';
|
||||
|
||||
if (response.status === 204 || response.headers.get('Content-Length') === '0') {
|
||||
let emptyData: any;
|
||||
let data: any;
|
||||
switch (parseAs) {
|
||||
case 'arrayBuffer':
|
||||
case 'blob':
|
||||
case 'text':
|
||||
emptyData = await response[parseAs]();
|
||||
break;
|
||||
case 'formData':
|
||||
emptyData = new FormData();
|
||||
case 'text':
|
||||
data = await response[parseAs]();
|
||||
break;
|
||||
case 'json': {
|
||||
// Some servers return 200 with no Content-Length and empty body.
|
||||
// response.json() would throw; read as text and parse if non-empty.
|
||||
const text = await response.text();
|
||||
data = text ? JSON.parse(text) : {};
|
||||
break;
|
||||
}
|
||||
case 'stream':
|
||||
emptyData = response.body;
|
||||
break;
|
||||
case 'json':
|
||||
default:
|
||||
emptyData = {};
|
||||
break;
|
||||
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'
|
||||
? emptyData
|
||||
? data
|
||||
: {
|
||||
data: emptyData,
|
||||
data,
|
||||
...result,
|
||||
};
|
||||
}
|
||||
|
||||
let data: any;
|
||||
switch (parseAs) {
|
||||
case 'arrayBuffer':
|
||||
case 'blob':
|
||||
case 'formData':
|
||||
case 'text':
|
||||
data = await response[parseAs]();
|
||||
break;
|
||||
case 'json': {
|
||||
// Some servers return 200 with no Content-Length and empty body.
|
||||
// response.json() would throw; read as text and parse if non-empty.
|
||||
const text = await response.text();
|
||||
data = text ? JSON.parse(text) : {};
|
||||
break;
|
||||
}
|
||||
case 'stream':
|
||||
return opts.responseStyle === 'data'
|
||||
? response.body
|
||||
: {
|
||||
data: response.body,
|
||||
...result,
|
||||
};
|
||||
const textError = await response.text();
|
||||
let jsonError: unknown;
|
||||
|
||||
try {
|
||||
jsonError = JSON.parse(textError);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
|
||||
if (parseAs === 'json') {
|
||||
if (opts.responseValidator) {
|
||||
await opts.responseValidator(data);
|
||||
}
|
||||
throw jsonError ?? textError;
|
||||
} catch (error) {
|
||||
let finalError = error;
|
||||
|
||||
if (opts.responseTransformer) {
|
||||
data = await opts.responseTransformer(data);
|
||||
for (const fn of interceptors.error.fns) {
|
||||
if (fn) {
|
||||
finalError = await fn(finalError, response, request, options as ResolvedRequestOptions);
|
||||
}
|
||||
}
|
||||
|
||||
return opts.responseStyle === 'data'
|
||||
? data
|
||||
finalError = finalError || {};
|
||||
|
||||
if (throwOnError) {
|
||||
throw finalError;
|
||||
}
|
||||
|
||||
// TODO: we probably want to return error and improve types
|
||||
return responseStyle === 'data'
|
||||
? undefined
|
||||
: {
|
||||
data,
|
||||
...result,
|
||||
error: finalError,
|
||||
request,
|
||||
response,
|
||||
};
|
||||
}
|
||||
|
||||
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 makeMethodFn = (method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
|
||||
|
|
@ -250,7 +230,6 @@ export const createClient = (config: Config = {}): Client => {
|
|||
return createSseClient({
|
||||
...opts,
|
||||
body: opts.body as BodyInit | null | undefined,
|
||||
headers: opts.headers as unknown as Record<string, string>,
|
||||
method,
|
||||
onRequest: async (url, init) => {
|
||||
let request = new Request(url, init);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ export {
|
|||
} from '../core/bodySerializer.gen';
|
||||
export { buildClientParams } from '../core/params.gen';
|
||||
export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen';
|
||||
export type { ServerSentEventsResult } from '../core/serverSentEvents.gen';
|
||||
export type { ClientMeta } from '../core/types.gen';
|
||||
export { createClient } from './client.gen';
|
||||
export type {
|
||||
Client,
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ export interface ResolvedRequestOptions<
|
|||
ThrowOnError extends boolean = boolean,
|
||||
Url extends string = string,
|
||||
> extends RequestOptions<unknown, TResponseStyle, ThrowOnError, Url> {
|
||||
headers: Headers;
|
||||
serializedBody?: string;
|
||||
}
|
||||
|
||||
|
|
@ -126,8 +127,10 @@ export type RequestResult<
|
|||
error: TError extends Record<string, unknown> ? TError[keyof TError] : TError;
|
||||
}
|
||||
) & {
|
||||
request: Request;
|
||||
response: Response;
|
||||
/** request may be undefined, because error may be from building the request object itself */
|
||||
request?: Request;
|
||||
/** response may be undefined, because error may be from building the request object itself or from a network error */
|
||||
response?: Response;
|
||||
}
|
||||
>;
|
||||
|
||||
|
|
@ -148,12 +151,13 @@ type MethodFn = <
|
|||
|
||||
type SseFn = <
|
||||
TData = unknown,
|
||||
TError = unknown,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_TError = unknown,
|
||||
ThrowOnError extends boolean = false,
|
||||
TResponseStyle extends ResponseStyle = 'fields',
|
||||
>(
|
||||
options: Omit<RequestOptions<never, TResponseStyle, ThrowOnError>, 'method'>,
|
||||
) => Promise<ServerSentEventsResult<TData, TError>>;
|
||||
) => Promise<ServerSentEventsResult<TData>>;
|
||||
|
||||
type RequestFn = <
|
||||
TData = unknown,
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'
|
|||
export const createQuerySerializer = <T = unknown>({
|
||||
parameters = {},
|
||||
...args
|
||||
}: QuerySerializerOptions = {}) => {
|
||||
const querySerializer = (queryParams: T) => {
|
||||
}: QuerySerializerOptions = {}): ((queryParams: T) => string) => {
|
||||
const querySerializer = (queryParams: T): string => {
|
||||
const search: string[] = [];
|
||||
if (queryParams && typeof queryParams === 'object') {
|
||||
for (const name in queryParams) {
|
||||
|
|
@ -118,14 +118,12 @@ const checkForExistence = (
|
|||
return false;
|
||||
};
|
||||
|
||||
export const setAuthParams = async ({
|
||||
security,
|
||||
...options
|
||||
}: Pick<Required<RequestOptions>, 'security'> &
|
||||
Pick<RequestOptions, 'auth' | 'query'> & {
|
||||
export async function setAuthParams(
|
||||
options: Pick<RequestOptions, 'auth' | 'query' | 'security'> & {
|
||||
headers: Headers;
|
||||
}) => {
|
||||
for (const auth of security) {
|
||||
},
|
||||
): Promise<void> {
|
||||
for (const auth of options.security ?? []) {
|
||||
if (checkForExistence(options, auth.name)) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -154,7 +152,7 @@ export const setAuthParams = async ({
|
|||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const buildUrl: Client['buildUrl'] = (options) =>
|
||||
getUrl({
|
||||
|
|
@ -218,8 +216,10 @@ export const mergeHeaders = (
|
|||
|
||||
type ErrInterceptor<Err, Res, Req, Options> = (
|
||||
error: Err,
|
||||
response: Res,
|
||||
request: Req,
|
||||
/** response may be undefined due to a network error where no response object is produced */
|
||||
response: Res | undefined,
|
||||
/** request may be undefined, because error may be from building the request object itself */
|
||||
request: Req | undefined,
|
||||
options: Options,
|
||||
) => Err | Promise<Err>;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,13 @@ export interface Auth {
|
|||
* @default 'header'
|
||||
*/
|
||||
in?: 'header' | 'query' | 'cookie';
|
||||
/**
|
||||
* A unique identifier for the security scheme.
|
||||
*
|
||||
* Defined only when there are multiple security schemes whose `Auth`
|
||||
* shape would otherwise be identical.
|
||||
*/
|
||||
key?: string;
|
||||
/**
|
||||
* Header or query parameter name.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ type KeyMap = Map<
|
|||
}
|
||||
>;
|
||||
|
||||
const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
|
||||
function buildKeyMap(fields: FieldsConfig, map?: KeyMap): KeyMap {
|
||||
if (!map) {
|
||||
map = new Map();
|
||||
}
|
||||
|
|
@ -85,33 +85,42 @@ const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
|
|||
}
|
||||
|
||||
return map;
|
||||
};
|
||||
}
|
||||
|
||||
interface Params {
|
||||
body: unknown;
|
||||
body?: unknown;
|
||||
headers: Record<string, unknown>;
|
||||
path: Record<string, unknown>;
|
||||
query: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const stripEmptySlots = (params: Params) => {
|
||||
function stripEmptySlots(params: Params): void {
|
||||
for (const [slot, value] of Object.entries(params)) {
|
||||
if (slot === 'body') continue;
|
||||
if (value && typeof value === 'object' && !Array.isArray(value) && !Object.keys(value).length) {
|
||||
delete params[slot as Slot];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsConfig) => {
|
||||
export function buildClientParams(args: ReadonlyArray<unknown>, fields: FieldsConfig): Params {
|
||||
const params: Params = {
|
||||
body: {},
|
||||
headers: {},
|
||||
path: {},
|
||||
query: {},
|
||||
headers: Object.create(null),
|
||||
path: Object.create(null),
|
||||
query: Object.create(null),
|
||||
};
|
||||
|
||||
const map = buildKeyMap(fields);
|
||||
|
||||
function writeSlot(slot: Slot, key: string, value: unknown): void {
|
||||
let record = params[slot] as Record<string, unknown> | undefined;
|
||||
if (record === undefined) {
|
||||
record = Object.create(null) as Record<string, unknown>;
|
||||
params[slot] = record;
|
||||
}
|
||||
record[key] = value;
|
||||
}
|
||||
|
||||
let config: FieldsConfig[number] | undefined;
|
||||
|
||||
for (const [index, arg] of args.entries()) {
|
||||
|
|
@ -128,7 +137,7 @@ export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsCo
|
|||
const field = map.get(config.key)!;
|
||||
const name = field.map || config.key;
|
||||
if (field.in) {
|
||||
(params[field.in] as Record<string, unknown>)[name] = arg;
|
||||
writeSlot(field.in, name, arg);
|
||||
}
|
||||
} else {
|
||||
params.body = arg;
|
||||
|
|
@ -140,7 +149,7 @@ export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsCo
|
|||
if (field) {
|
||||
if (field.in) {
|
||||
const name = field.map || key;
|
||||
(params[field.in] as Record<string, unknown>)[name] = value;
|
||||
writeSlot(field.in, name, value);
|
||||
} else {
|
||||
params[field.map] = value;
|
||||
}
|
||||
|
|
@ -149,11 +158,11 @@ export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsCo
|
|||
|
||||
if (extra) {
|
||||
const [prefix, slot] = extra;
|
||||
(params[slot] as Record<string, unknown>)[key.slice(prefix.length)] = value;
|
||||
writeSlot(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 as Slot] as Record<string, unknown>)[key] = value;
|
||||
writeSlot(slot as Slot, key, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -166,4 +175,4 @@ export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsCo
|
|||
stripEmptySlots(params);
|
||||
|
||||
return params;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ interface SerializePrimitiveParam extends SerializePrimitiveOptions {
|
|||
value: string;
|
||||
}
|
||||
|
||||
export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
|
||||
export const separatorArrayExplode = (style: ArraySeparatorStyle): '.' | ';' | ',' | '&' => {
|
||||
switch (style) {
|
||||
case 'label':
|
||||
return '.';
|
||||
|
|
@ -38,7 +38,7 @@ export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
|
||||
export const separatorArrayNoExplode = (style: ArraySeparatorStyle): ',' | '|' | '%20' => {
|
||||
switch (style) {
|
||||
case 'form':
|
||||
return ',';
|
||||
|
|
@ -51,7 +51,7 @@ export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const separatorObjectExplode = (style: ObjectSeparatorStyle) => {
|
||||
export const separatorObjectExplode = (style: ObjectSeparatorStyle): '.' | ';' | ',' | '&' => {
|
||||
switch (style) {
|
||||
case 'label':
|
||||
return '.';
|
||||
|
|
@ -72,7 +72,7 @@ export const serializeArrayParam = ({
|
|||
value,
|
||||
}: SerializeOptions<ArraySeparatorStyle> & {
|
||||
value: unknown[];
|
||||
}) => {
|
||||
}): string => {
|
||||
if (!explode) {
|
||||
const joinedValues = (
|
||||
allowReserved ? value : value.map((v) => encodeURIComponent(v as string))
|
||||
|
|
@ -110,7 +110,7 @@ export const serializePrimitiveParam = ({
|
|||
allowReserved,
|
||||
name,
|
||||
value,
|
||||
}: SerializePrimitiveParam) => {
|
||||
}: SerializePrimitiveParam): string => {
|
||||
if (value === undefined || value === null) {
|
||||
return '';
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ export const serializeObjectParam = ({
|
|||
}: SerializeOptions<ObjectSeparatorStyle> & {
|
||||
value: Record<string, unknown> | Date;
|
||||
valueOnly?: boolean;
|
||||
}) => {
|
||||
}): string => {
|
||||
if (value instanceof Date) {
|
||||
return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export type JsonValue =
|
|||
/**
|
||||
* Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes.
|
||||
*/
|
||||
export const queryKeyJsonReplacer = (_key: string, value: unknown) => {
|
||||
export const queryKeyJsonReplacer = (_key: string, value: unknown): unknown | undefined => {
|
||||
if (value === undefined || typeof value === 'function' || typeof value === 'symbol') {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ export type ServerSentEventsResult<TData = unknown, TReturn = void, TNext = unkn
|
|||
>;
|
||||
};
|
||||
|
||||
export const createSseClient = <TData = unknown>({
|
||||
export function createSseClient<TData = unknown>({
|
||||
onRequest,
|
||||
onSseError,
|
||||
onSseEvent,
|
||||
|
|
@ -91,7 +91,7 @@ export const createSseClient = <TData = unknown>({
|
|||
sseSleepFn,
|
||||
url,
|
||||
...options
|
||||
}: ServerSentEventsOptions): ServerSentEventsResult<TData> => {
|
||||
}: ServerSentEventsOptions): ServerSentEventsResult<TData> {
|
||||
let lastEventId: string | undefined;
|
||||
|
||||
const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));
|
||||
|
|
@ -155,8 +155,7 @@ export const createSseClient = <TData = unknown>({
|
|||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += value;
|
||||
// Normalize line endings: CRLF -> LF, then CR -> LF
|
||||
buffer = buffer.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||
buffer = buffer.replace(/\r\n?/g, '\n'); // normalize line endings
|
||||
|
||||
const chunks = buffer.split('\n\n');
|
||||
buffer = chunks.pop() ?? '';
|
||||
|
|
@ -240,4 +239,4 @@ export const createSseClient = <TData = unknown>({
|
|||
const stream = createStream();
|
||||
|
||||
return { stream };
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,12 @@ export interface Config {
|
|||
responseValidator?: (data: unknown) => Promise<unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arbitrary metadata passed through the `meta` request option.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
export interface ClientMeta {}
|
||||
|
||||
type IsExactlyNeverOrNeverUndefined<T> = [T] extends [never]
|
||||
? true
|
||||
: [T] extends [never | undefined]
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ export interface PathSerializer {
|
|||
url: string;
|
||||
}
|
||||
|
||||
export const PATH_PARAM_RE = /\{[^{}]+\}/g;
|
||||
export const PATH_PARAM_RE: RegExp = /\{[^{}]+\}/g;
|
||||
|
||||
export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
|
||||
export const defaultPathSerializer = ({ path, url: _url }: PathSerializer): string => {
|
||||
let url = _url;
|
||||
const matches = _url.match(PATH_PARAM_RE);
|
||||
if (matches) {
|
||||
|
|
@ -94,7 +94,7 @@ export const getUrl = ({
|
|||
query?: Record<string, unknown>;
|
||||
querySerializer: QuerySerializer;
|
||||
url: string;
|
||||
}) => {
|
||||
}): string => {
|
||||
const pathUrl = _url.startsWith('/') ? _url : `/${_url}`;
|
||||
let url = (baseUrl ?? '') + pathUrl;
|
||||
if (path) {
|
||||
|
|
@ -114,7 +114,7 @@ export function getValidRequestBody(options: {
|
|||
body?: unknown;
|
||||
bodySerializer?: BodySerializer | null;
|
||||
serializedBody?: unknown;
|
||||
}) {
|
||||
}): unknown {
|
||||
const hasBody = options.body !== undefined;
|
||||
const isSerializedBody = hasBody && options.bodySerializer;
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -5804,7 +5804,7 @@ export type TransferCallConfig = {
|
|||
/**
|
||||
* Destination
|
||||
*
|
||||
* Phone number or SIP endpoint to transfer the call to, e.g. +1234567890 or PJSIP/1234.
|
||||
* Phone number, SIP endpoint, or template to transfer the call to, e.g. +1234567890, PJSIP/1234, or {{initial_context.transfer_destination}}.
|
||||
*/
|
||||
destination: string;
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue