stitchkit 0.47.0 → 0.48.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +9 -0
  2. package/dist/browser/client.d.ts +3 -0
  3. package/dist/browser/client.d.ts.map +1 -1
  4. package/dist/browser/http.d.ts +7 -4
  5. package/dist/browser/http.d.ts.map +1 -1
  6. package/dist/cli.js +3 -2
  7. package/dist/contract/define.d.ts +11 -3
  8. package/dist/contract/define.d.ts.map +1 -1
  9. package/dist/{index-nrytvb30.js → index-03j2t778.js} +5 -8
  10. package/dist/{index-kp8xamqp.js → index-0hj37z43.js} +4 -2
  11. package/dist/index-48ffdxgk.js +6 -0
  12. package/dist/index-h05ygjqx.js +149 -0
  13. package/dist/{index-ee621cmy.js → index-jewp9r0a.js} +5 -139
  14. package/dist/index-p9d4cxt5.js +436 -0
  15. package/dist/{index-44xysy8r.js → index-ts21eyz4.js} +141 -19
  16. package/dist/{index-8ekq6res.js → index-v58mwa19.js} +4 -2
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +40 -27
  20. package/dist/node.d.ts +1 -1
  21. package/dist/node.d.ts.map +1 -1
  22. package/dist/node.js +8 -3
  23. package/dist/observability/audit.d.ts +4 -1
  24. package/dist/observability/audit.d.ts.map +1 -1
  25. package/dist/observability/index.d.ts +1 -0
  26. package/dist/observability/index.d.ts.map +1 -1
  27. package/dist/observability/index.js +150 -16
  28. package/dist/observability/status.d.ts +105 -0
  29. package/dist/observability/status.d.ts.map +1 -0
  30. package/dist/server/implement.d.ts +16 -6
  31. package/dist/server/implement.d.ts.map +1 -1
  32. package/dist/server/index.d.ts +1 -1
  33. package/dist/server/index.d.ts.map +1 -1
  34. package/dist/server/index.js +14 -6
  35. package/dist/server/router.d.ts +4 -1
  36. package/dist/server/router.d.ts.map +1 -1
  37. package/dist/testing.d.ts +34 -0
  38. package/dist/testing.d.ts.map +1 -0
  39. package/dist/testing.js +36 -0
  40. package/dist/tools/mcp.d.ts +1 -0
  41. package/dist/tools/mcp.d.ts.map +1 -1
  42. package/dist/tools.js +21 -430
  43. package/llms-full.txt +177 -22
  44. package/package.json +7 -3
package/dist/tools.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import {
2
- inputIsQuery,
3
2
  signJwt,
4
3
  verifyPkce
5
- } from "./index-nrytvb30.js";
4
+ } from "./index-03j2t778.js";
6
5
  import {
7
6
  DEFAULT_CORS_ALLOW_HEADERS
8
7
  } from "./index-r1qp4rve.js";
@@ -26,26 +25,31 @@ import {
26
25
  toolErrorFromResult,
27
26
  toolResultFromError,
28
27
  writeDownload
29
- } from "./index-kp8xamqp.js";
28
+ } from "./index-0hj37z43.js";
30
29
  import {
31
30
  isWithinDir,
32
31
  realPathWithinDir,
33
32
  toJsonSchema
34
33
  } from "./index-yxpe3phd.js";
34
+ import {
35
+ ApiError,
36
+ createClient
37
+ } from "./index-p9d4cxt5.js";
38
+ import"./index-48ffdxgk.js";
35
39
  import {
36
40
  redact
37
- } from "./index-8ekq6res.js";
41
+ } from "./index-v58mwa19.js";
38
42
  import {
39
43
  AppError,
40
44
  getRequestContext,
41
45
  getTraceId,
42
- isRecord,
43
46
  mergeMeta,
44
- parseTrailingWildcard,
45
- resolvePropagationContext,
46
- runWithRequestContext,
47
- typedEntries
48
- } from "./index-ee621cmy.js";
47
+ runWithRequestContext
48
+ } from "./index-jewp9r0a.js";
49
+ import {
50
+ isRecord,
51
+ resolvePropagationContext
52
+ } from "./index-h05ygjqx.js";
49
53
 
50
54
  // src/tools/agent.ts
51
55
  import { jsonSchema, tool } from "ai";
@@ -851,7 +855,7 @@ function prepareMcpTool(mountable, config, validation, logger, failures, seen) {
851
855
  if (allowed.has(path))
852
856
  continue;
853
857
  const clue = untyped.description ? ` (only a description: "${untyped.description}")` : "";
854
- reportIncompatible(`MCP tool "${mountable.name}" — input property "${untyped.path}" carries no type, enum or $ref${clue}. ` + "A model is given no way to know what to send. Widen the contract, or list it in `allowUntyped` if it is deliberately free-form.", policy === "skip" ? "warn" : policy, logger, failures);
858
+ reportIncompatible(`MCP tool "${mountable.name}" — input property "${untyped.path}" carries no type, enum or $ref${clue}. ` + "A model is given no way to know what to send. Use `z.json()` for an arbitrary JSON value; use `allowUntyped` only when the presentation value is genuinely not representable as JSON Schema.", policy === "skip" ? "warn" : policy, logger, failures);
855
859
  }
856
860
  }
857
861
  if (validation.requirePortableFormats) {
@@ -1098,6 +1102,9 @@ function mountPreparedMcp(mcpServer, prepared, config = {}) {
1098
1102
  }
1099
1103
  }
1100
1104
  function buildMcpServer(config, auth) {
1105
+ if (!isMcpServerBuildConfig(config)) {
1106
+ throw new TypeError("[stitchkit] buildMcpServer requires a valid config object");
1107
+ }
1101
1108
  let surface;
1102
1109
  if (config.surfaces) {
1103
1110
  const key = config.selectSurface(auth);
@@ -1125,6 +1132,9 @@ function buildMcpServer(config, auth) {
1125
1132
  });
1126
1133
  return buildMcpServerFromPrepared(config, auth, prepared);
1127
1134
  }
1135
+ function isMcpServerBuildConfig(value) {
1136
+ return isRecord(value) && isRecord(value.serverInfo);
1137
+ }
1128
1138
  function buildMcpServerFromPrepared(config, auth, prepared) {
1129
1139
  let roundCodec;
1130
1140
  if (config.multiRound) {
@@ -2158,425 +2168,6 @@ function mountOAuthProvider(config) {
2158
2168
  };
2159
2169
  return dcrEnabled ? [metadataRoute, registerRoute, authorizeRoute, tokenRoute] : [metadataRoute, authorizeRoute, tokenRoute];
2160
2170
  }
2161
- // src/browser/cancellation.ts
2162
- class RequestCancellationError extends Error {
2163
- cause;
2164
- constructor(cause) {
2165
- super(cause === "caller" ? "Request was aborted" : "Request timed out");
2166
- this.cause = cause;
2167
- this.name = "RequestCancellationError";
2168
- }
2169
- }
2170
- function createRequestCancellation(caller, timeoutMs) {
2171
- if (!caller && timeoutMs === undefined) {
2172
- return { signal: undefined, run: (operation) => operation() };
2173
- }
2174
- const controller = new AbortController;
2175
- let cause;
2176
- let timer;
2177
- const abortFromCaller = () => {
2178
- if (cause)
2179
- return;
2180
- cause = "caller";
2181
- controller.abort(caller?.reason);
2182
- };
2183
- if (caller?.aborted)
2184
- abortFromCaller();
2185
- else
2186
- caller?.addEventListener("abort", abortFromCaller, { once: true });
2187
- if (timeoutMs !== undefined) {
2188
- timer = setTimeout(() => {
2189
- if (cause)
2190
- return;
2191
- cause = "timeout";
2192
- controller.abort(new DOMException("Request timed out", "TimeoutError"));
2193
- }, timeoutMs);
2194
- }
2195
- return {
2196
- signal: controller.signal,
2197
- async run(operation) {
2198
- try {
2199
- if (cause === "caller")
2200
- throw cancellationError(cause);
2201
- return await operation(controller.signal);
2202
- } catch (error) {
2203
- if (cause)
2204
- throw cancellationError(cause);
2205
- throw error;
2206
- } finally {
2207
- if (timer !== undefined)
2208
- clearTimeout(timer);
2209
- caller?.removeEventListener("abort", abortFromCaller);
2210
- }
2211
- }
2212
- };
2213
- }
2214
- function cancellationError(cause) {
2215
- return new RequestCancellationError(cause);
2216
- }
2217
-
2218
- // src/browser/client-multipart.ts
2219
- function isFileDescriptor(value) {
2220
- return typeof value === "object" && value !== null && !(value instanceof Blob) && "uri" in value && typeof value.uri === "string" && "name" in value && typeof value.name === "string" && "type" in value && typeof value.type === "string";
2221
- }
2222
- function isMultipartFile(value) {
2223
- return value instanceof Blob || isFileDescriptor(value);
2224
- }
2225
- function appendMultipartFile(form, field, file) {
2226
- const sink = form;
2227
- sink.append(field, file);
2228
- }
2229
- function appendFormFields(formData, values, skipKeys) {
2230
- for (const [key, value] of Object.entries(values)) {
2231
- if (skipKeys.has(key) || value === undefined || value === null)
2232
- continue;
2233
- formData.append(key, typeof value === "string" ? value : JSON.stringify(value));
2234
- }
2235
- }
2236
- function buildMultipartForm(descriptor, values) {
2237
- const formData = new FormData;
2238
- const fileFields = new Set(Object.keys(descriptor.files));
2239
- for (const [field, policy] of Object.entries(descriptor.files)) {
2240
- const value = values[field];
2241
- if (value === undefined) {
2242
- if (policy.required !== false)
2243
- throw new Error(`Missing multipart file field: ${field}`);
2244
- continue;
2245
- }
2246
- if (policy.multiple === true) {
2247
- if (!Array.isArray(value) || value.length === 0) {
2248
- throw new Error(`Multipart file field "${field}" must be a non-empty array`);
2249
- }
2250
- for (const file of value) {
2251
- if (!isMultipartFile(file)) {
2252
- throw new Error(`Invalid multipart file field: ${field}`);
2253
- }
2254
- appendMultipartFile(formData, field, file);
2255
- }
2256
- continue;
2257
- }
2258
- if (!isMultipartFile(value))
2259
- throw new Error(`Invalid multipart file field: ${field}`);
2260
- appendMultipartFile(formData, field, value);
2261
- }
2262
- appendFormFields(formData, values, fileFields);
2263
- return formData;
2264
- }
2265
-
2266
- // src/browser/client-url.ts
2267
- function isParamArray(value) {
2268
- return Array.isArray(value) && value.every((item) => typeof item === "string" || typeof item === "number");
2269
- }
2270
- function collectQueryParams(args, endpoint) {
2271
- const params = {};
2272
- let hasParams = false;
2273
- for (const [key, value] of Object.entries(args)) {
2274
- if (value === undefined || value === null)
2275
- continue;
2276
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || isParamArray(value)) {
2277
- params[key] = value;
2278
- hasParams = true;
2279
- continue;
2280
- }
2281
- const what = Array.isArray(value) ? "an array with non-primitive items" : typeof value === "object" ? "a nested object" : `a ${typeof value}`;
2282
- throw new Error(`${endpoint.method} ${endpoint.path}: input field "${key}" is ${what} — it cannot ` + "travel as a query parameter. GET / DELETE input must be flat (string / number / " + "boolean, or an array of string / number); flatten the field or move the " + "operation to a body verb (POST).");
2283
- }
2284
- return hasParams ? params : undefined;
2285
- }
2286
- function hasStringKeys(args, keys) {
2287
- for (const key of keys) {
2288
- if (typeof args[key] !== "string")
2289
- return false;
2290
- }
2291
- return true;
2292
- }
2293
- function resolvePathPrefix(config, args) {
2294
- if (!config?.pathPrefix)
2295
- return "";
2296
- if (typeof config.pathPrefix === "string")
2297
- return config.pathPrefix;
2298
- const keys = config.stripPrefixKeys ?? [];
2299
- if (!hasStringKeys(args, keys)) {
2300
- const missing = keys.find((key) => typeof args[key] !== "string");
2301
- throw new Error(`Missing path prefix key: ${missing}`);
2302
- }
2303
- return config.pathPrefix(args);
2304
- }
2305
- function extractParamNames(path) {
2306
- const matches = path.match(/:(\w+)/g);
2307
- const names = matches ? matches.map((match) => match.slice(1)) : [];
2308
- const wildcard = parseTrailingWildcard(path);
2309
- if (wildcard)
2310
- names.push(wildcard.name);
2311
- return names;
2312
- }
2313
- function fillPathParams(path, args) {
2314
- const wildcard = parseTrailingWildcard(path);
2315
- let filled = path.replace(/:(\w+)/g, (_, key) => {
2316
- const value = args[key];
2317
- if (value === undefined || value === null) {
2318
- throw new Error(`Missing path param: ${key}`);
2319
- }
2320
- return encodeURIComponent(String(value));
2321
- });
2322
- if (!wildcard)
2323
- return filled;
2324
- const wildcardValue = args[wildcard.name];
2325
- if (wildcardValue === undefined || wildcardValue === null) {
2326
- throw new Error(`Missing path param: ${wildcard.name}`);
2327
- }
2328
- const remainder = String(wildcardValue).split("/").map((segment) => encodeURIComponent(segment)).join("/");
2329
- filled = `${filled.slice(0, -(wildcard.name.length + 1))}${remainder}`;
2330
- return filled;
2331
- }
2332
- function stripConsumedArgs(args, path, scopeKeys) {
2333
- const consumed = new Set(scopeKeys);
2334
- for (const name of extractParamNames(path))
2335
- consumed.add(name);
2336
- const remaining = {};
2337
- for (const [key, value] of Object.entries(args)) {
2338
- if (!consumed.has(key) && value !== undefined)
2339
- remaining[key] = value;
2340
- }
2341
- return remaining;
2342
- }
2343
- function appendQuery(relativeUrl, params) {
2344
- if (!params)
2345
- return relativeUrl;
2346
- const search = new URLSearchParams;
2347
- for (const [key, value] of Object.entries(params)) {
2348
- if (Array.isArray(value)) {
2349
- for (const item of value)
2350
- search.append(key, String(item));
2351
- } else {
2352
- search.set(key, String(value));
2353
- }
2354
- }
2355
- return search.size > 0 ? `${relativeUrl}?${search}` : relativeUrl;
2356
- }
2357
- function planClientRequest(endpoint, contractPrefix, args, config) {
2358
- let pathPrefix = resolvePathPrefix(config, args);
2359
- if (pathPrefix && !pathPrefix.endsWith("/"))
2360
- pathPrefix += "/";
2361
- if (pathPrefix.startsWith("/"))
2362
- pathPrefix = pathPrefix.slice(1);
2363
- const endpointPath = endpoint.path === "/" ? "" : endpoint.path;
2364
- let relativeUrl = fillPathParams(`${pathPrefix}${contractPrefix}${endpointPath}`, args);
2365
- if (relativeUrl.endsWith("/"))
2366
- relativeUrl = relativeUrl.slice(0, -1);
2367
- const remainingArgs = stripConsumedArgs(args, endpoint.path, config?.stripPrefixKeys ?? []);
2368
- if (inputIsQuery(endpoint.method)) {
2369
- relativeUrl = appendQuery(relativeUrl, collectQueryParams(remainingArgs, endpoint));
2370
- }
2371
- return { relativeUrl, remainingArgs };
2372
- }
2373
- function joinClientBaseUrl(baseUrl, relativeUrl) {
2374
- const base = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
2375
- const path = relativeUrl.startsWith("/") ? relativeUrl : `/${relativeUrl}`;
2376
- return `${base}${path}`;
2377
- }
2378
-
2379
- // src/browser/http.ts
2380
- import ky, { isHTTPError } from "ky";
2381
-
2382
- // src/browser/request-id.ts
2383
- var REQUEST_ID_HEADER = "x-request-id";
2384
- function responseTraceId(response) {
2385
- return response?.headers.get(REQUEST_ID_HEADER) ?? undefined;
2386
- }
2387
-
2388
- // src/browser/http.ts
2389
- class ApiError extends Error {
2390
- code;
2391
- status;
2392
- details;
2393
- hint;
2394
- traceId;
2395
- constructor(code, status = 0, details, message, hint, traceId) {
2396
- super(message ?? `API Error: ${code}`);
2397
- this.code = code;
2398
- this.status = status;
2399
- this.details = details;
2400
- this.hint = hint;
2401
- this.traceId = traceId;
2402
- this.name = "ApiError";
2403
- }
2404
- static is(error) {
2405
- return error instanceof ApiError;
2406
- }
2407
- }
2408
- function parseApiErrorBody(body) {
2409
- if (!isRecord(body) || !isRecord(body.error))
2410
- return null;
2411
- const error = body.error;
2412
- if (typeof error.code !== "string")
2413
- return null;
2414
- return {
2415
- code: error.code,
2416
- message: typeof error.message === "string" ? error.message : undefined,
2417
- details: error.details,
2418
- hint: typeof error.hint === "string" ? error.hint : undefined
2419
- };
2420
- }
2421
-
2422
- // src/browser/client.ts
2423
- function withTimeout(options, endpoint) {
2424
- const responseType = endpoint.rawResponse ? "response" : endpoint.output ? undefined : "void";
2425
- if (endpoint.timeout === undefined && responseType === undefined)
2426
- return options;
2427
- return {
2428
- ...options,
2429
- ...endpoint.timeout !== undefined && { timeout: endpoint.timeout },
2430
- ...responseType && { responseType }
2431
- };
2432
- }
2433
- function withOutput(endpoint, result) {
2434
- if (endpoint.rawResponse)
2435
- return result;
2436
- const schema = endpoint.output;
2437
- return result.then((value) => {
2438
- if (!schema) {
2439
- if (value === undefined || value === null)
2440
- return;
2441
- throw new Error("Server returned data for an endpoint with no output contract");
2442
- }
2443
- if (value === undefined) {
2444
- throw new Error("Server returned no body for an endpoint with an output contract");
2445
- }
2446
- return schema.parse(value);
2447
- });
2448
- }
2449
- function createClient(contract, configOrClient, contractConfig) {
2450
- const client = {};
2451
- const makeMethod = isHttpAdapter(configOrClient) ? (endpoint) => createHttpMethod(endpoint, contract.meta.prefix, configOrClient, contractConfig) : (endpoint) => createFetchMethod(endpoint, contract.meta.prefix, configOrClient, contractConfig);
2452
- for (const [key, endpoint] of typedEntries(contract.endpoints)) {
2453
- if (endpoint.expose && !endpoint.expose.includes("HTTP"))
2454
- continue;
2455
- setClientMethod(client, key, makeMethod(endpoint));
2456
- }
2457
- return client;
2458
- }
2459
- function isHttpAdapter(value) {
2460
- return typeof value === "object" && "get" in value && typeof value.get === "function";
2461
- }
2462
- function setClientMethod(target, key, method) {
2463
- target[key] = method;
2464
- }
2465
- function createHttpMethod(endpoint, prefix, client, config) {
2466
- const httpMethod = endpoint.method.toLowerCase();
2467
- return (...args) => {
2468
- const { requestArgs: firstArg, options } = splitClientCall(endpoint, args, config);
2469
- const plan = planClientRequest(endpoint, prefix, firstArg, config);
2470
- if (endpoint.multipart) {
2471
- if (httpMethod === "get" || httpMethod === "head" || httpMethod === "delete") {
2472
- throw new Error(`Multipart endpoint ${endpoint.method} ${endpoint.path} must be POST / PUT / PATCH`);
2473
- }
2474
- const formData = buildMultipartForm(endpoint.multipart, plan.remainingArgs);
2475
- return withOutput(endpoint, client[httpMethod](plan.relativeUrl, formData, withTimeout(options, endpoint)));
2476
- }
2477
- if (httpMethod === "get" || httpMethod === "head") {
2478
- return withOutput(endpoint, client[httpMethod](plan.relativeUrl, withTimeout(options, endpoint)));
2479
- }
2480
- if (httpMethod === "delete") {
2481
- return withOutput(endpoint, client.delete(plan.relativeUrl, withTimeout(options, endpoint)));
2482
- }
2483
- return withOutput(endpoint, client[httpMethod](plan.relativeUrl, Object.keys(plan.remainingArgs).length > 0 ? plan.remainingArgs : undefined, withTimeout(options, endpoint)));
2484
- };
2485
- }
2486
- function createFetchMethod(endpoint, prefix, config, contractConfig) {
2487
- return async (...callArgs) => {
2488
- const { requestArgs, options } = splitClientCall(endpoint, callArgs, contractConfig);
2489
- const plan = planClientRequest(endpoint, prefix, requestArgs, contractConfig);
2490
- const url = joinClientBaseUrl(config.baseUrl, plan.relativeUrl);
2491
- const headers = {
2492
- Accept: "application/json",
2493
- ...typeof config.headers === "function" ? config.headers() : config.headers
2494
- };
2495
- const cancellation = createRequestCancellation(options?.signal, endpoint.timeout ?? config.timeout ?? 30000);
2496
- const hasBody = endpoint.method !== "GET" && endpoint.method !== "HEAD" && endpoint.method !== "DELETE" && !endpoint.multipart && endpoint.input && Object.keys(plan.remainingArgs).length > 0;
2497
- if (hasBody)
2498
- headers["Content-Type"] = "application/json";
2499
- try {
2500
- return await cancellation.run(async (signal) => {
2501
- const body = endpoint.multipart ? buildMultipartForm(endpoint.multipart, plan.remainingArgs) : hasBody ? JSON.stringify(plan.remainingArgs) : undefined;
2502
- const res = await fetch(url, {
2503
- method: endpoint.method,
2504
- headers,
2505
- credentials: config.credentials,
2506
- signal,
2507
- ...body !== undefined && { body }
2508
- });
2509
- if (!res.ok) {
2510
- await throwForErrorResponse(res, config, { error: res.statusText });
2511
- }
2512
- if (endpoint.rawResponse)
2513
- return res;
2514
- if (!endpoint.output) {
2515
- const text = await res.text();
2516
- if (text.length > 0) {
2517
- throw new Error("Server returned data for an endpoint with no output contract");
2518
- }
2519
- return;
2520
- }
2521
- return endpoint.output.parse(await res.json());
2522
- });
2523
- } catch (error) {
2524
- if (error instanceof RequestCancellationError) {
2525
- throw new ApiError(error.cause === "caller" ? "REQUEST_ABORTED" : "REQUEST_TIMEOUT", 0, undefined, error.message);
2526
- }
2527
- if (ApiError.is(error))
2528
- throw error;
2529
- const message = error instanceof Error ? error.message : undefined;
2530
- throw new ApiError("UNKNOWN_ERROR", 0, message ? { message } : undefined, message);
2531
- }
2532
- };
2533
- }
2534
- function endpointHasArguments(endpoint) {
2535
- return Boolean(endpoint.params || endpoint.input || endpoint.multipart);
2536
- }
2537
- function splitClientCall(endpoint, args, contractConfig) {
2538
- const hasScopedArguments = (contractConfig?.stripPrefixKeys?.length ?? 0) > 0;
2539
- if (!endpointHasArguments(endpoint) && !hasScopedArguments) {
2540
- return {
2541
- requestArgs: {},
2542
- options: readClientRequestOptions(args[0])
2543
- };
2544
- }
2545
- const requestArgs = args[0];
2546
- if (requestArgs !== undefined && !isRecord(requestArgs)) {
2547
- throw new TypeError("Endpoint arguments must be an object");
2548
- }
2549
- return {
2550
- requestArgs: requestArgs ?? {},
2551
- options: readClientRequestOptions(args[1])
2552
- };
2553
- }
2554
- function readClientRequestOptions(value) {
2555
- if (value === undefined)
2556
- return;
2557
- if (!isRecord(value))
2558
- throw new TypeError("Client request options must be an object");
2559
- const signal = value.signal;
2560
- if (signal === undefined)
2561
- return {};
2562
- if (!isAbortSignal(signal)) {
2563
- throw new TypeError("Client request signal must be an AbortSignal");
2564
- }
2565
- return { signal };
2566
- }
2567
- function isAbortSignal(value) {
2568
- return typeof value === "object" && value !== null && "aborted" in value && typeof value.aborted === "boolean" && "addEventListener" in value && typeof value.addEventListener === "function" && "removeEventListener" in value && typeof value.removeEventListener === "function";
2569
- }
2570
- async function throwForErrorResponse(res, config, fallbackBody) {
2571
- const body = await res.json().catch(() => fallbackBody);
2572
- config.onError?.(res.status, body);
2573
- const parsed = parseApiErrorBody(body);
2574
- if (parsed) {
2575
- throw new ApiError(parsed.code, res.status, parsed.details, parsed.message, parsed.hint, responseTraceId(res));
2576
- }
2577
- throw new ApiError("HTTP_ERROR", res.status, { body }, undefined, undefined, responseTraceId(res));
2578
- }
2579
-
2580
2171
  // src/tools/remote.ts
2581
2172
  var HTTP_ONLY = Object.freeze(["HTTP"]);
2582
2173
  function toArgs(ctx) {