stitchkit 0.46.0 → 0.48.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 +13 -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 +67 -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 +195 -94
- 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 +171 -74
- package/llms-full.txt +411 -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) {
|
|
@@ -2362,11 +2448,11 @@ function withOutput(endpoint, result) {
|
|
|
2362
2448
|
}
|
|
2363
2449
|
function createClient(contract, configOrClient, contractConfig) {
|
|
2364
2450
|
const client = {};
|
|
2365
|
-
const
|
|
2451
|
+
const makeExecutor = isHttpAdapter(configOrClient) ? (endpoint) => createHttpExecutor(endpoint, contract.meta.prefix, configOrClient, contractConfig) : (endpoint) => createFetchExecutor(endpoint, contract.meta.prefix, configOrClient, contractConfig);
|
|
2366
2452
|
for (const [key, endpoint] of typedEntries(contract.endpoints)) {
|
|
2367
2453
|
if (endpoint.expose && !endpoint.expose.includes("HTTP"))
|
|
2368
2454
|
continue;
|
|
2369
|
-
setClientMethod(client, key,
|
|
2455
|
+
setClientMethod(client, key, createEndpointMethod(endpoint, makeExecutor(endpoint), contractConfig));
|
|
2370
2456
|
}
|
|
2371
2457
|
return client;
|
|
2372
2458
|
}
|
|
@@ -2376,98 +2462,109 @@ function isHttpAdapter(value) {
|
|
|
2376
2462
|
function setClientMethod(target, key, method) {
|
|
2377
2463
|
target[key] = method;
|
|
2378
2464
|
}
|
|
2379
|
-
function
|
|
2465
|
+
function createEndpointMethod(endpoint, execute, contractConfig) {
|
|
2466
|
+
const hasScopedArguments = (contractConfig?.stripPrefixKeys?.length ?? 0) > 0;
|
|
2467
|
+
if (endpointHasArguments(endpoint) || hasScopedArguments) {
|
|
2468
|
+
const method2 = (requestArgs) => execute(readClientRequestArgs(requestArgs), undefined);
|
|
2469
|
+
return Object.assign(method2, {
|
|
2470
|
+
withOptions: (requestArgs, options) => execute(readClientRequestArgs(requestArgs), readClientRequestOptions(options))
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
const method = () => execute({}, undefined);
|
|
2474
|
+
return Object.assign(method, {
|
|
2475
|
+
withOptions: (options) => execute({}, readClientRequestOptions(options))
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
function createHttpExecutor(endpoint, prefix, client, config) {
|
|
2380
2479
|
const httpMethod = endpoint.method.toLowerCase();
|
|
2381
|
-
return (
|
|
2382
|
-
const
|
|
2383
|
-
const plan = planClientRequest(endpoint, prefix, firstArg, config);
|
|
2480
|
+
return (requestArgs, options) => {
|
|
2481
|
+
const plan = planClientRequest(endpoint, prefix, requestArgs, config);
|
|
2384
2482
|
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
2483
|
if (httpMethod === "get" || httpMethod === "head" || httpMethod === "delete") {
|
|
2390
2484
|
throw new Error(`Multipart endpoint ${endpoint.method} ${endpoint.path} must be POST / PUT / PATCH`);
|
|
2391
2485
|
}
|
|
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)));
|
|
2486
|
+
const formData = buildMultipartForm(endpoint.multipart, plan.remainingArgs);
|
|
2487
|
+
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, formData, withTimeout(options, endpoint)));
|
|
2396
2488
|
}
|
|
2397
2489
|
if (httpMethod === "get" || httpMethod === "head") {
|
|
2398
|
-
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, withTimeout(
|
|
2490
|
+
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, withTimeout(options, endpoint)));
|
|
2399
2491
|
}
|
|
2400
2492
|
if (httpMethod === "delete") {
|
|
2401
|
-
return withOutput(endpoint, client.delete(plan.relativeUrl, withTimeout(
|
|
2493
|
+
return withOutput(endpoint, client.delete(plan.relativeUrl, withTimeout(options, endpoint)));
|
|
2402
2494
|
}
|
|
2403
|
-
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, Object.keys(plan.remainingArgs).length > 0 ? plan.remainingArgs : undefined, withTimeout(
|
|
2495
|
+
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, Object.keys(plan.remainingArgs).length > 0 ? plan.remainingArgs : undefined, withTimeout(options, endpoint)));
|
|
2404
2496
|
};
|
|
2405
2497
|
}
|
|
2406
|
-
function
|
|
2407
|
-
return async (
|
|
2408
|
-
const plan = planClientRequest(endpoint, prefix,
|
|
2498
|
+
function createFetchExecutor(endpoint, prefix, config, contractConfig) {
|
|
2499
|
+
return async (requestArgs, options) => {
|
|
2500
|
+
const plan = planClientRequest(endpoint, prefix, requestArgs, contractConfig);
|
|
2409
2501
|
const url = joinClientBaseUrl(config.baseUrl, plan.relativeUrl);
|
|
2410
2502
|
const headers = {
|
|
2411
2503
|
Accept: "application/json",
|
|
2412
2504
|
...typeof config.headers === "function" ? config.headers() : config.headers
|
|
2413
2505
|
};
|
|
2414
|
-
const
|
|
2415
|
-
const hasBody = endpoint.method !== "GET" && endpoint.method !== "HEAD" && endpoint.method !== "DELETE" && !endpoint.multipart && endpoint.input &&
|
|
2506
|
+
const cancellation = createRequestCancellation(options?.signal, endpoint.timeout ?? config.timeout ?? 30000);
|
|
2507
|
+
const hasBody = endpoint.method !== "GET" && endpoint.method !== "HEAD" && endpoint.method !== "DELETE" && !endpoint.multipart && endpoint.input && Object.keys(plan.remainingArgs).length > 0;
|
|
2416
2508
|
if (hasBody)
|
|
2417
2509
|
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");
|
|
2510
|
+
try {
|
|
2511
|
+
return await cancellation.run(async (signal) => {
|
|
2512
|
+
const body = endpoint.multipart ? buildMultipartForm(endpoint.multipart, plan.remainingArgs) : hasBody ? JSON.stringify(plan.remainingArgs) : undefined;
|
|
2513
|
+
const res = await fetch(url, {
|
|
2514
|
+
method: endpoint.method,
|
|
2515
|
+
headers,
|
|
2516
|
+
credentials: config.credentials,
|
|
2517
|
+
signal,
|
|
2518
|
+
...body !== undefined && { body }
|
|
2519
|
+
});
|
|
2520
|
+
if (!res.ok) {
|
|
2521
|
+
await throwForErrorResponse(res, config, { error: res.statusText });
|
|
2442
2522
|
}
|
|
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");
|
|
2523
|
+
if (endpoint.rawResponse)
|
|
2524
|
+
return res;
|
|
2525
|
+
if (!endpoint.output) {
|
|
2526
|
+
const text = await res.text();
|
|
2527
|
+
if (text.length > 0) {
|
|
2528
|
+
throw new Error("Server returned data for an endpoint with no output contract");
|
|
2529
|
+
}
|
|
2530
|
+
return;
|
|
2531
|
+
}
|
|
2532
|
+
return endpoint.output.parse(await res.json());
|
|
2533
|
+
});
|
|
2534
|
+
} catch (error) {
|
|
2535
|
+
if (error instanceof RequestCancellationError) {
|
|
2536
|
+
throw new ApiError(error.cause === "caller" ? "REQUEST_ABORTED" : "REQUEST_TIMEOUT", 0, undefined, error.message);
|
|
2465
2537
|
}
|
|
2466
|
-
|
|
2538
|
+
if (ApiError.is(error))
|
|
2539
|
+
throw error;
|
|
2540
|
+
const message = error instanceof Error ? error.message : undefined;
|
|
2541
|
+
throw new ApiError("UNKNOWN_ERROR", 0, message ? { message } : undefined, message);
|
|
2467
2542
|
}
|
|
2468
|
-
return endpoint.output.parse(await res.json());
|
|
2469
2543
|
};
|
|
2470
2544
|
}
|
|
2545
|
+
function endpointHasArguments(endpoint) {
|
|
2546
|
+
return Boolean(endpoint.params || endpoint.input || endpoint.multipart);
|
|
2547
|
+
}
|
|
2548
|
+
function readClientRequestArgs(requestArgs) {
|
|
2549
|
+
if (requestArgs !== undefined && !isRecord(requestArgs)) {
|
|
2550
|
+
throw new TypeError("Endpoint arguments must be an object");
|
|
2551
|
+
}
|
|
2552
|
+
return requestArgs ?? {};
|
|
2553
|
+
}
|
|
2554
|
+
function readClientRequestOptions(value) {
|
|
2555
|
+
if (!isRecord(value))
|
|
2556
|
+
throw new TypeError("Client request options must be an object");
|
|
2557
|
+
const signal = value.signal;
|
|
2558
|
+
if (signal === undefined)
|
|
2559
|
+
return {};
|
|
2560
|
+
if (!isAbortSignal(signal)) {
|
|
2561
|
+
throw new TypeError("Client request signal must be an AbortSignal");
|
|
2562
|
+
}
|
|
2563
|
+
return { signal };
|
|
2564
|
+
}
|
|
2565
|
+
function isAbortSignal(value) {
|
|
2566
|
+
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";
|
|
2567
|
+
}
|
|
2471
2568
|
async function throwForErrorResponse(res, config, fallbackBody) {
|
|
2472
2569
|
const body = await res.json().catch(() => fallbackBody);
|
|
2473
2570
|
config.onError?.(res.status, body);
|