stitchkit 0.46.0 → 0.47.0
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.
- package/README.md +4 -3
- package/dist/browser/cancellation.d.ts +14 -0
- package/dist/browser/cancellation.d.ts.map +1 -0
- package/dist/browser/client-multipart.d.ts +3 -1
- package/dist/browser/client-multipart.d.ts.map +1 -1
- package/dist/browser/client.d.ts +1 -0
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/http.d.ts +1 -0
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/contract/define.d.ts +59 -15
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +1 -1
- package/dist/{index-zwqty9zf.js → index-44xysy8r.js} +465 -49
- package/dist/{index-pwyedf7b.js → index-45dz4m51.js} +46 -0
- package/dist/{index-c40tkxcd.js → index-8ekq6res.js} +1 -1
- package/dist/{index-5s8b7z6q.js → index-ee621cmy.js} +9 -9
- package/dist/{index-62pqb23z.js → index-kp8xamqp.js} +1 -1
- package/dist/{index-w1s873ng.js → index-nrytvb30.js} +4 -3
- package/dist/index.js +191 -88
- package/dist/node.js +2 -2
- package/dist/observability/audit.d.ts +22 -3
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +82 -15
- package/dist/server/context.d.ts +8 -5
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/implement.d.ts +33 -2
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +28 -6
- package/dist/server/middleware/auth.d.ts +7 -4
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/multipart.d.ts +13 -18
- package/dist/server/multipart.d.ts.map +1 -1
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/types.d.ts +48 -15
- package/dist/server/types.d.ts.map +1 -1
- package/dist/tools.js +167 -68
- package/llms-full.txt +367 -67
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
inputIsQuery,
|
|
3
3
|
signJwt,
|
|
4
4
|
verifyPkce
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-nrytvb30.js";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_CORS_ALLOW_HEADERS
|
|
8
8
|
} from "./index-r1qp4rve.js";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
toolErrorFromResult,
|
|
27
27
|
toolResultFromError,
|
|
28
28
|
writeDownload
|
|
29
|
-
} from "./index-
|
|
29
|
+
} from "./index-kp8xamqp.js";
|
|
30
30
|
import {
|
|
31
31
|
isWithinDir,
|
|
32
32
|
realPathWithinDir,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
} from "./index-yxpe3phd.js";
|
|
35
35
|
import {
|
|
36
36
|
redact
|
|
37
|
-
} from "./index-
|
|
37
|
+
} from "./index-8ekq6res.js";
|
|
38
38
|
import {
|
|
39
39
|
AppError,
|
|
40
40
|
getRequestContext,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
resolvePropagationContext,
|
|
46
46
|
runWithRequestContext,
|
|
47
47
|
typedEntries
|
|
48
|
-
} from "./index-
|
|
48
|
+
} from "./index-ee621cmy.js";
|
|
49
49
|
|
|
50
50
|
// src/tools/agent.ts
|
|
51
51
|
import { jsonSchema, tool } from "ai";
|
|
@@ -2158,6 +2158,63 @@ function mountOAuthProvider(config) {
|
|
|
2158
2158
|
};
|
|
2159
2159
|
return dcrEnabled ? [metadataRoute, registerRoute, authorizeRoute, tokenRoute] : [metadataRoute, authorizeRoute, tokenRoute];
|
|
2160
2160
|
}
|
|
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
|
+
|
|
2161
2218
|
// src/browser/client-multipart.ts
|
|
2162
2219
|
function isFileDescriptor(value) {
|
|
2163
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";
|
|
@@ -2176,6 +2233,35 @@ function appendFormFields(formData, values, skipKeys) {
|
|
|
2176
2233
|
formData.append(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
2177
2234
|
}
|
|
2178
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
|
+
}
|
|
2179
2265
|
|
|
2180
2266
|
// src/browser/client-url.ts
|
|
2181
2267
|
function isParamArray(value) {
|
|
@@ -2379,95 +2465,108 @@ function setClientMethod(target, key, method) {
|
|
|
2379
2465
|
function createHttpMethod(endpoint, prefix, client, config) {
|
|
2380
2466
|
const httpMethod = endpoint.method.toLowerCase();
|
|
2381
2467
|
return (...args) => {
|
|
2382
|
-
const firstArg = args
|
|
2468
|
+
const { requestArgs: firstArg, options } = splitClientCall(endpoint, args, config);
|
|
2383
2469
|
const plan = planClientRequest(endpoint, prefix, firstArg, config);
|
|
2384
2470
|
if (endpoint.multipart) {
|
|
2385
|
-
const file = firstArg[endpoint.multipart];
|
|
2386
|
-
if (!isMultipartFile(file)) {
|
|
2387
|
-
throw new Error(`Missing multipart file field: ${endpoint.multipart}`);
|
|
2388
|
-
}
|
|
2389
2471
|
if (httpMethod === "get" || httpMethod === "head" || httpMethod === "delete") {
|
|
2390
2472
|
throw new Error(`Multipart endpoint ${endpoint.method} ${endpoint.path} must be POST / PUT / PATCH`);
|
|
2391
2473
|
}
|
|
2392
|
-
const formData =
|
|
2393
|
-
|
|
2394
|
-
appendFormFields(formData, plan.remainingArgs, new Set([endpoint.multipart]));
|
|
2395
|
-
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, formData, withTimeout(undefined, endpoint)));
|
|
2474
|
+
const formData = buildMultipartForm(endpoint.multipart, plan.remainingArgs);
|
|
2475
|
+
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, formData, withTimeout(options, endpoint)));
|
|
2396
2476
|
}
|
|
2397
2477
|
if (httpMethod === "get" || httpMethod === "head") {
|
|
2398
|
-
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, withTimeout(
|
|
2478
|
+
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, withTimeout(options, endpoint)));
|
|
2399
2479
|
}
|
|
2400
2480
|
if (httpMethod === "delete") {
|
|
2401
|
-
return withOutput(endpoint, client.delete(plan.relativeUrl, withTimeout(
|
|
2481
|
+
return withOutput(endpoint, client.delete(plan.relativeUrl, withTimeout(options, endpoint)));
|
|
2402
2482
|
}
|
|
2403
|
-
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, Object.keys(plan.remainingArgs).length > 0 ? plan.remainingArgs : undefined, withTimeout(
|
|
2483
|
+
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, Object.keys(plan.remainingArgs).length > 0 ? plan.remainingArgs : undefined, withTimeout(options, endpoint)));
|
|
2404
2484
|
};
|
|
2405
2485
|
}
|
|
2406
2486
|
function createFetchMethod(endpoint, prefix, config, contractConfig) {
|
|
2407
|
-
return async (
|
|
2408
|
-
const
|
|
2487
|
+
return async (...callArgs) => {
|
|
2488
|
+
const { requestArgs, options } = splitClientCall(endpoint, callArgs, contractConfig);
|
|
2489
|
+
const plan = planClientRequest(endpoint, prefix, requestArgs, contractConfig);
|
|
2409
2490
|
const url = joinClientBaseUrl(config.baseUrl, plan.relativeUrl);
|
|
2410
2491
|
const headers = {
|
|
2411
2492
|
Accept: "application/json",
|
|
2412
2493
|
...typeof config.headers === "function" ? config.headers() : config.headers
|
|
2413
2494
|
};
|
|
2414
|
-
const
|
|
2415
|
-
const hasBody = endpoint.method !== "GET" && endpoint.method !== "HEAD" && endpoint.method !== "DELETE" && !endpoint.multipart && endpoint.input &&
|
|
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;
|
|
2416
2497
|
if (hasBody)
|
|
2417
2498
|
headers["Content-Type"] = "application/json";
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
body: formData,
|
|
2431
|
-
signal
|
|
2432
|
-
});
|
|
2433
|
-
if (!res2.ok) {
|
|
2434
|
-
await throwForErrorResponse(res2, config, null);
|
|
2435
|
-
}
|
|
2436
|
-
if (endpoint.rawResponse)
|
|
2437
|
-
return res2;
|
|
2438
|
-
if (!endpoint.output) {
|
|
2439
|
-
const text = await res2.text();
|
|
2440
|
-
if (text.length > 0) {
|
|
2441
|
-
throw new Error("Server returned data for an endpoint with no output contract");
|
|
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 });
|
|
2442
2511
|
}
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
await throwForErrorResponse(res, config, { error: res.statusText });
|
|
2458
|
-
}
|
|
2459
|
-
if (endpoint.rawResponse)
|
|
2460
|
-
return res;
|
|
2461
|
-
if (!endpoint.output) {
|
|
2462
|
-
const text = await res.text();
|
|
2463
|
-
if (text.length > 0) {
|
|
2464
|
-
throw new Error("Server returned data for an endpoint with no output contract");
|
|
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);
|
|
2465
2526
|
}
|
|
2466
|
-
|
|
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);
|
|
2467
2531
|
}
|
|
2468
|
-
return endpoint.output.parse(await res.json());
|
|
2469
2532
|
};
|
|
2470
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
|
+
}
|
|
2471
2570
|
async function throwForErrorResponse(res, config, fallbackBody) {
|
|
2472
2571
|
const body = await res.json().catch(() => fallbackBody);
|
|
2473
2572
|
config.onError?.(res.status, body);
|