stitchkit 0.67.0 → 0.68.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/dist/agent-runtime.js +5 -6
- package/dist/application/admission.d.ts +131 -0
- package/dist/application/admission.d.ts.map +1 -0
- package/dist/application/channel.d.ts +106 -0
- package/dist/application/channel.d.ts.map +1 -0
- package/dist/application/latest-sink.d.ts.map +1 -1
- package/dist/application-grammy.js +1 -1
- package/dist/application-opentelemetry.js +1 -1
- package/dist/application.d.ts +2 -0
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +386 -17
- package/dist/browser/client.d.ts +2 -3
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/contract-stream.d.ts +5 -0
- package/dist/browser/contract-stream.d.ts.map +1 -0
- package/dist/browser/http.d.ts +3 -0
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/stream.d.ts +4 -1
- package/dist/browser/stream.d.ts.map +1 -1
- package/dist/browser/transport.d.ts +3 -0
- package/dist/browser/transport.d.ts.map +1 -0
- package/dist/cli.js +6 -7
- package/dist/contract/define.d.ts +34 -2
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/errors.d.ts +4 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.d.ts +2 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +5 -1
- package/dist/contract/stream.d.ts +19 -0
- package/dist/contract/stream.d.ts.map +1 -0
- package/dist/{index-xxye8j3k.js → index-0w9abg87.js} +4 -0
- package/dist/index-69m2278y.js +115 -0
- package/dist/{index-physbf4z.js → index-6wzr93cg.js} +1 -1
- package/dist/{index-1pgeyyee.js → index-7pn02bgv.js} +4 -6
- package/dist/index-9ky9hhg3.js +484 -0
- package/dist/{index-wrhf06ak.js → index-avjd67h6.js} +8 -8
- package/dist/{index-413xk7ga.js → index-cq9q73nf.js} +50 -1
- package/dist/{index-6taryy00.js → index-dafax5md.js} +1 -1
- package/dist/{index-8mxadm34.js → index-nymn7wbg.js} +1092 -19
- package/dist/{index-y91zd0ch.js → index-pr0qsmjy.js} +3 -2
- package/dist/index-v5bayx1z.js +138 -0
- package/dist/{index-mfw1pec7.js → index-wqyrvhzz.js} +3 -3
- package/dist/{index-svqhcrrj.js → index-zd4v31v2.js} +156 -10
- package/dist/{index-x8v8b7rc.js → index-zpyj7hsv.js} +330 -58
- package/dist/index.js +270 -105
- package/dist/internal/bounded-lines.d.ts +4 -0
- package/dist/internal/bounded-lines.d.ts.map +1 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +11 -8
- package/dist/observability/index.js +6 -7
- package/dist/remote.js +5 -6
- package/dist/server/contract-stream.d.ts +5 -0
- package/dist/server/contract-stream.d.ts.map +1 -0
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +41 -283
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/stream.d.ts +3 -1
- package/dist/server/stream.d.ts.map +1 -1
- package/dist/server/streaming-route.d.ts +2 -0
- package/dist/server/streaming-route.d.ts.map +1 -1
- package/dist/server/types.d.ts +13 -3
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/unix-client-body.d.ts +4 -0
- package/dist/server/unix-client-body.d.ts.map +1 -0
- package/dist/server/unix-client-bun.d.ts +13 -0
- package/dist/server/unix-client-bun.d.ts.map +1 -0
- package/dist/server/unix-client-error.d.ts +9 -0
- package/dist/server/unix-client-error.d.ts.map +1 -0
- package/dist/server/unix-client.d.ts +25 -0
- package/dist/server/unix-client.d.ts.map +1 -0
- package/dist/testing.js +6 -5
- package/dist/tools.js +14 -16
- package/llms-full.txt +337 -17
- package/package.json +1 -1
- package/dist/index-48ffdxgk.js +0 -6
- package/dist/index-pzyt11ch.js +0 -127
- package/dist/index-tvwcrx2d.js +0 -224
- package/dist/index-wd8g8z6e.js +0 -206
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isUnsafeKey,
|
|
3
3
|
mergeMeta
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-9ky9hhg3.js";
|
|
5
5
|
import {
|
|
6
6
|
callRuntimeHandler,
|
|
7
7
|
isRecord,
|
|
@@ -144,11 +144,12 @@ function bindContract(contract, handlers) {
|
|
|
144
144
|
serviceName: contract.meta.prefix,
|
|
145
145
|
key: String(key),
|
|
146
146
|
toolName: "toolName" in endpoint ? endpoint.toolName : undefined,
|
|
147
|
-
expose: endpoint.rawResponse || endpoint.rawBody || endpoint.responseMeta ? HTTP_ONLY : endpoint.expose,
|
|
147
|
+
expose: endpoint.rawResponse || endpoint.rawBody || endpoint.responseMeta || "stream" in endpoint && endpoint.stream ? HTTP_ONLY : endpoint.expose,
|
|
148
148
|
scope: endpoint.scope ?? groupScope,
|
|
149
149
|
paramsSchema: endpoint.params,
|
|
150
150
|
inputSchema: endpoint.input,
|
|
151
151
|
outputSchema: endpoint.output,
|
|
152
|
+
stream: "stream" in endpoint ? endpoint.stream : undefined,
|
|
152
153
|
multipart: endpoint.multipart,
|
|
153
154
|
multipartReceivers: streamingHandler?.receivers,
|
|
154
155
|
maxJsonBodyBytes: endpoint.maxJsonBodyBytes,
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeError
|
|
3
|
+
} from "./index-9ky9hhg3.js";
|
|
4
|
+
|
|
5
|
+
// src/internal/bounded-lines.ts
|
|
6
|
+
var DEFAULT_STREAM_LINE_BYTES = 1024 * 1024;
|
|
7
|
+
function lineLimit(value) {
|
|
8
|
+
const resolved = value ?? DEFAULT_STREAM_LINE_BYTES;
|
|
9
|
+
if (!Number.isSafeInteger(resolved) || resolved <= 0) {
|
|
10
|
+
throw new TypeError("maxLineBytes must be a positive safe integer");
|
|
11
|
+
}
|
|
12
|
+
return resolved;
|
|
13
|
+
}
|
|
14
|
+
function joinBytes(left, right) {
|
|
15
|
+
if (left.byteLength === 0)
|
|
16
|
+
return right.slice();
|
|
17
|
+
const result = new Uint8Array(left.byteLength + right.byteLength);
|
|
18
|
+
result.set(left);
|
|
19
|
+
result.set(right, left.byteLength);
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
async function* readBoundedUtf8Lines(response, maxLineBytes) {
|
|
23
|
+
const reader = response.body?.getReader();
|
|
24
|
+
if (!reader)
|
|
25
|
+
return;
|
|
26
|
+
const limit = lineLimit(maxLineBytes);
|
|
27
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
28
|
+
let pending = new Uint8Array;
|
|
29
|
+
try {
|
|
30
|
+
for (;; ) {
|
|
31
|
+
const chunk = await reader.read();
|
|
32
|
+
if (chunk.done)
|
|
33
|
+
break;
|
|
34
|
+
let start = 0;
|
|
35
|
+
for (let index = 0;index < chunk.value.byteLength; index += 1) {
|
|
36
|
+
if (chunk.value[index] !== 10)
|
|
37
|
+
continue;
|
|
38
|
+
const lineBytes = joinBytes(pending, chunk.value.slice(start, index));
|
|
39
|
+
if (lineBytes.byteLength > limit) {
|
|
40
|
+
throw new RangeError(`Stream line exceeds the ${limit} byte limit`);
|
|
41
|
+
}
|
|
42
|
+
yield decoder.decode(lineBytes);
|
|
43
|
+
pending = new Uint8Array;
|
|
44
|
+
start = index + 1;
|
|
45
|
+
}
|
|
46
|
+
pending = joinBytes(pending, chunk.value.slice(start));
|
|
47
|
+
if (pending.byteLength > limit) {
|
|
48
|
+
throw new RangeError(`Stream line exceeds the ${limit} byte limit`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (pending.byteLength > 0)
|
|
52
|
+
yield decoder.decode(pending);
|
|
53
|
+
} finally {
|
|
54
|
+
await reader.cancel().catch(() => {
|
|
55
|
+
return;
|
|
56
|
+
});
|
|
57
|
+
reader.releaseLock();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/server/stream.ts
|
|
62
|
+
function streamSSE(generator) {
|
|
63
|
+
const encoder = new TextEncoder;
|
|
64
|
+
let cancelled = false;
|
|
65
|
+
const stream = new ReadableStream({
|
|
66
|
+
async start(controller) {
|
|
67
|
+
try {
|
|
68
|
+
for await (const chunk of generator) {
|
|
69
|
+
if (cancelled)
|
|
70
|
+
return;
|
|
71
|
+
const data = JSON.stringify(chunk);
|
|
72
|
+
controller.enqueue(encoder.encode(`data: ${data}
|
|
73
|
+
|
|
74
|
+
`));
|
|
75
|
+
}
|
|
76
|
+
if (cancelled)
|
|
77
|
+
return;
|
|
78
|
+
controller.enqueue(encoder.encode(`data: [DONE]
|
|
79
|
+
|
|
80
|
+
`));
|
|
81
|
+
controller.close();
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (cancelled)
|
|
84
|
+
return;
|
|
85
|
+
const envelope = normalizeError(err).toJSON();
|
|
86
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(envelope)}
|
|
87
|
+
|
|
88
|
+
`));
|
|
89
|
+
controller.close();
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
cancel() {
|
|
93
|
+
cancelled = true;
|
|
94
|
+
generator.return(undefined).catch(() => {
|
|
95
|
+
return;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return new Response(stream, {
|
|
100
|
+
headers: {
|
|
101
|
+
"Content-Type": "text/event-stream",
|
|
102
|
+
"Cache-Control": "no-cache",
|
|
103
|
+
Connection: "keep-alive"
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async function* parseSSE(response, options) {
|
|
108
|
+
try {
|
|
109
|
+
for await (const rawLine of readBoundedUtf8Lines(response, options?.maxLineBytes ?? DEFAULT_STREAM_LINE_BYTES)) {
|
|
110
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
111
|
+
if (!line.startsWith("data:"))
|
|
112
|
+
continue;
|
|
113
|
+
const data = line.slice(5).replace(/^ /, "");
|
|
114
|
+
if (data === "[DONE]")
|
|
115
|
+
return;
|
|
116
|
+
try {
|
|
117
|
+
yield JSON.parse(data);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
120
|
+
if (!options?.onParseError)
|
|
121
|
+
throw failure;
|
|
122
|
+
options.onParseError(data, failure);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
127
|
+
if (!options?.onParseError)
|
|
128
|
+
throw failure;
|
|
129
|
+
options.onParseError("", failure);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/internal/http-input.ts
|
|
134
|
+
function inputIsQuery(method) {
|
|
135
|
+
return method === "GET" || method === "DELETE";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { inputIsQuery, DEFAULT_STREAM_LINE_BYTES, readBoundedUtf8Lines, streamSSE, parseSSE };
|
|
@@ -2,20 +2,20 @@ import {
|
|
|
2
2
|
contextContributionDelta,
|
|
3
3
|
mergeContextContribution,
|
|
4
4
|
prepareContextContribution
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-pr0qsmjy.js";
|
|
6
6
|
import {
|
|
7
7
|
base64UrlToBytes,
|
|
8
8
|
bytesToBase64Url,
|
|
9
9
|
isUnsafeKey,
|
|
10
10
|
safeJsonParse
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-9ky9hhg3.js";
|
|
12
12
|
import {
|
|
13
13
|
isRecord
|
|
14
14
|
} from "./index-smpbdg6k.js";
|
|
15
15
|
import {
|
|
16
16
|
forbidden,
|
|
17
17
|
unauthorized
|
|
18
|
-
} from "./index-
|
|
18
|
+
} from "./index-0w9abg87.js";
|
|
19
19
|
|
|
20
20
|
// src/server/middleware/cookies.ts
|
|
21
21
|
function parseCookies(header) {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
DEFAULT_STREAM_LINE_BYTES,
|
|
3
|
+
inputIsQuery,
|
|
4
|
+
parseSSE,
|
|
5
|
+
readBoundedUtf8Lines
|
|
6
|
+
} from "./index-v5bayx1z.js";
|
|
4
7
|
import {
|
|
8
|
+
ContractStreamFrameSchema,
|
|
9
|
+
DEFAULT_CONTRACT_STREAM_FRAME_BYTES,
|
|
5
10
|
parseTrailingWildcard
|
|
6
|
-
} from "./index-
|
|
11
|
+
} from "./index-9ky9hhg3.js";
|
|
7
12
|
import {
|
|
8
13
|
isRecord,
|
|
9
14
|
mapObject,
|
|
@@ -277,10 +282,103 @@ function parseApiErrorBody(body) {
|
|
|
277
282
|
hint: typeof error.hint === "string" ? error.hint : undefined
|
|
278
283
|
};
|
|
279
284
|
}
|
|
285
|
+
// src/browser/stream.ts
|
|
286
|
+
function parseFailure(raw, error, onParseError) {
|
|
287
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
288
|
+
if (!onParseError)
|
|
289
|
+
throw failure;
|
|
290
|
+
onParseError(raw, failure);
|
|
291
|
+
}
|
|
292
|
+
async function* parseNDJSON(response, options) {
|
|
293
|
+
try {
|
|
294
|
+
for await (const rawLine of readBoundedUtf8Lines(response, options?.maxLineBytes ?? DEFAULT_STREAM_LINE_BYTES)) {
|
|
295
|
+
const line = (rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine).trim();
|
|
296
|
+
if (line === "")
|
|
297
|
+
continue;
|
|
298
|
+
try {
|
|
299
|
+
yield JSON.parse(line);
|
|
300
|
+
} catch (error) {
|
|
301
|
+
parseFailure(line, error, options?.onParseError);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
parseFailure("", error, options?.onParseError);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/browser/contract-stream.ts
|
|
310
|
+
function sourceFor(response, descriptor) {
|
|
311
|
+
const options = {
|
|
312
|
+
maxLineBytes: descriptor.maxFrameBytes ?? DEFAULT_CONTRACT_STREAM_FRAME_BYTES
|
|
313
|
+
};
|
|
314
|
+
return descriptor.format === "sse" ? parseSSE(response, options) : parseNDJSON(response, options);
|
|
315
|
+
}
|
|
316
|
+
async function* readContractStream(response, descriptor, abort) {
|
|
317
|
+
let ended = false;
|
|
318
|
+
let terminalSeen = descriptor.terminal === undefined;
|
|
319
|
+
try {
|
|
320
|
+
for await (const raw of sourceFor(response, descriptor)) {
|
|
321
|
+
const parsedFrame = ContractStreamFrameSchema.safeParse(raw);
|
|
322
|
+
if (!parsedFrame.success) {
|
|
323
|
+
throw new ApiError("STREAM_PROTOCOL_ERROR", 0, undefined, "Stream frame did not match the protocol envelope");
|
|
324
|
+
}
|
|
325
|
+
const frame = parsedFrame.data;
|
|
326
|
+
if (frame.type === "error") {
|
|
327
|
+
throw new ApiError(frame.error.code, 0, frame.error.details, frame.error.message, frame.error.hint);
|
|
328
|
+
}
|
|
329
|
+
if (frame.type === "end") {
|
|
330
|
+
if (!terminalSeen) {
|
|
331
|
+
throw new ApiError("STREAM_TERMINAL_MISSING", 0, undefined, "Stream completed before its declared terminal item");
|
|
332
|
+
}
|
|
333
|
+
ended = true;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const parsedItem = descriptor.item.safeParse(frame.data);
|
|
337
|
+
if (!parsedItem.success) {
|
|
338
|
+
throw new ApiError("STREAM_ITEM_INVALID", 0, undefined, "Stream item did not match its contract");
|
|
339
|
+
}
|
|
340
|
+
const item = parsedItem.data;
|
|
341
|
+
if (descriptor.terminal?.safeParse(item).success)
|
|
342
|
+
terminalSeen = true;
|
|
343
|
+
yield item;
|
|
344
|
+
}
|
|
345
|
+
if (!ended) {
|
|
346
|
+
throw new ApiError("STREAM_TRUNCATED", 0, undefined, "Stream ended without its protocol terminal frame");
|
|
347
|
+
}
|
|
348
|
+
} finally {
|
|
349
|
+
abort();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
class OwnedContractStream {
|
|
354
|
+
#inner;
|
|
355
|
+
#abort;
|
|
356
|
+
constructor(inner, abort) {
|
|
357
|
+
this.#inner = inner;
|
|
358
|
+
this.#abort = abort;
|
|
359
|
+
}
|
|
360
|
+
[Symbol.asyncIterator]() {
|
|
361
|
+
return this;
|
|
362
|
+
}
|
|
363
|
+
next() {
|
|
364
|
+
return this.#inner.next();
|
|
365
|
+
}
|
|
366
|
+
return() {
|
|
367
|
+
this.#abort();
|
|
368
|
+
return this.#inner.return(undefined);
|
|
369
|
+
}
|
|
370
|
+
throw(error) {
|
|
371
|
+
this.#abort();
|
|
372
|
+
return this.#inner.throw(error);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function parseContractStream(response, descriptor, abort) {
|
|
376
|
+
return new OwnedContractStream(readContractStream(response, descriptor, abort), abort);
|
|
377
|
+
}
|
|
280
378
|
|
|
281
379
|
// src/browser/client.ts
|
|
282
380
|
function withTimeout(options, endpoint) {
|
|
283
|
-
const responseType = endpoint.rawResponse ? "response" : endpoint.output ? undefined : "void";
|
|
381
|
+
const responseType = endpoint.rawResponse || "stream" in endpoint ? "response" : endpoint.output ? undefined : "void";
|
|
284
382
|
if (endpoint.timeout === undefined && responseType === undefined)
|
|
285
383
|
return options;
|
|
286
384
|
return {
|
|
@@ -289,9 +387,23 @@ function withTimeout(options, endpoint) {
|
|
|
289
387
|
...responseType && { responseType }
|
|
290
388
|
};
|
|
291
389
|
}
|
|
292
|
-
function withOutput(endpoint, result) {
|
|
390
|
+
function withOutput(endpoint, result, abortStream) {
|
|
293
391
|
if (endpoint.rawResponse)
|
|
294
392
|
return result;
|
|
393
|
+
if ("stream" in endpoint && endpoint.stream) {
|
|
394
|
+
return result.then((value) => {
|
|
395
|
+
if (!(value instanceof Response)) {
|
|
396
|
+
abortStream?.();
|
|
397
|
+
throw new Error("Streaming endpoint did not return a Response");
|
|
398
|
+
}
|
|
399
|
+
return parseContractStream(value, endpoint.stream, abortStream ?? (() => {
|
|
400
|
+
return;
|
|
401
|
+
}));
|
|
402
|
+
}, (error) => {
|
|
403
|
+
abortStream?.();
|
|
404
|
+
throw error;
|
|
405
|
+
});
|
|
406
|
+
}
|
|
295
407
|
const schema = endpoint.output;
|
|
296
408
|
return result.then((value) => {
|
|
297
409
|
if (!schema) {
|
|
@@ -341,20 +453,26 @@ function createHttpExecutor(endpoint, prefix, client, config) {
|
|
|
341
453
|
const httpMethod = endpoint.method.toLowerCase();
|
|
342
454
|
return (requestArgs, options) => {
|
|
343
455
|
const plan = planClientRequest(endpoint, prefix, requestArgs, config);
|
|
456
|
+
const streamAbort = "stream" in endpoint ? new AbortController : undefined;
|
|
457
|
+
const requestOptions = streamAbort ? {
|
|
458
|
+
...options,
|
|
459
|
+
signal: options?.signal ? AbortSignal.any([options.signal, streamAbort.signal]) : streamAbort.signal
|
|
460
|
+
} : options;
|
|
461
|
+
const finishStream = streamAbort ? () => streamAbort.abort() : undefined;
|
|
344
462
|
if (endpoint.multipart) {
|
|
345
463
|
if (httpMethod === "get" || httpMethod === "head" || httpMethod === "delete") {
|
|
346
464
|
throw new Error(`Multipart endpoint ${endpoint.method} ${endpoint.path} must be POST / PUT / PATCH`);
|
|
347
465
|
}
|
|
348
466
|
const formData = buildMultipartForm(endpoint.multipart, plan.remainingArgs);
|
|
349
|
-
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, formData, withTimeout(
|
|
467
|
+
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, formData, withTimeout(requestOptions, endpoint)), finishStream);
|
|
350
468
|
}
|
|
351
469
|
if (httpMethod === "get" || httpMethod === "head") {
|
|
352
|
-
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, withTimeout(
|
|
470
|
+
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, withTimeout(requestOptions, endpoint)), finishStream);
|
|
353
471
|
}
|
|
354
472
|
if (httpMethod === "delete") {
|
|
355
|
-
return withOutput(endpoint, client.delete(plan.relativeUrl, withTimeout(
|
|
473
|
+
return withOutput(endpoint, client.delete(plan.relativeUrl, withTimeout(requestOptions, endpoint)), finishStream);
|
|
356
474
|
}
|
|
357
|
-
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, Object.keys(plan.remainingArgs).length > 0 ? plan.remainingArgs : undefined, withTimeout(
|
|
475
|
+
return withOutput(endpoint, client[httpMethod](plan.relativeUrl, Object.keys(plan.remainingArgs).length > 0 ? plan.remainingArgs : undefined, withTimeout(requestOptions, endpoint)), finishStream);
|
|
358
476
|
};
|
|
359
477
|
}
|
|
360
478
|
function createFetchExecutor(endpoint, prefix, config, contractConfig) {
|
|
@@ -366,7 +484,32 @@ function createFetchExecutor(endpoint, prefix, config, contractConfig) {
|
|
|
366
484
|
Accept: "application/json",
|
|
367
485
|
...typeof config.headers === "function" ? config.headers() : config.headers
|
|
368
486
|
};
|
|
369
|
-
const
|
|
487
|
+
const streamAbort = "stream" in endpoint ? new AbortController : undefined;
|
|
488
|
+
const requestSignal = streamAbort ? options?.signal ? AbortSignal.any([options.signal, streamAbort.signal]) : streamAbort.signal : options?.signal;
|
|
489
|
+
const openTimeoutMs = endpoint.timeout ?? config.timeout ?? 30000;
|
|
490
|
+
const cancellation = streamAbort ? {
|
|
491
|
+
signal: requestSignal,
|
|
492
|
+
async run(operation) {
|
|
493
|
+
let timedOut = false;
|
|
494
|
+
const timer = setTimeout(() => {
|
|
495
|
+
timedOut = true;
|
|
496
|
+
streamAbort.abort(new DOMException("Request timed out", "TimeoutError"));
|
|
497
|
+
}, openTimeoutMs);
|
|
498
|
+
try {
|
|
499
|
+
if (options?.signal?.aborted)
|
|
500
|
+
throw new RequestCancellationError("caller");
|
|
501
|
+
return await operation(requestSignal);
|
|
502
|
+
} catch (error) {
|
|
503
|
+
if (timedOut)
|
|
504
|
+
throw new RequestCancellationError("timeout");
|
|
505
|
+
if (options?.signal?.aborted)
|
|
506
|
+
throw new RequestCancellationError("caller");
|
|
507
|
+
throw error;
|
|
508
|
+
} finally {
|
|
509
|
+
clearTimeout(timer);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
} : createRequestCancellation(requestSignal, openTimeoutMs);
|
|
370
513
|
const hasBody = endpoint.method !== "GET" && endpoint.method !== "HEAD" && endpoint.method !== "DELETE" && !endpoint.multipart && endpoint.input && Object.keys(plan.remainingArgs).length > 0;
|
|
371
514
|
if (hasBody)
|
|
372
515
|
headers["Content-Type"] = "application/json";
|
|
@@ -385,6 +528,9 @@ function createFetchExecutor(endpoint, prefix, config, contractConfig) {
|
|
|
385
528
|
}
|
|
386
529
|
if (endpoint.rawResponse)
|
|
387
530
|
return res;
|
|
531
|
+
if ("stream" in endpoint && endpoint.stream) {
|
|
532
|
+
return parseContractStream(res, endpoint.stream, () => streamAbort?.abort());
|
|
533
|
+
}
|
|
388
534
|
if (!endpoint.output) {
|
|
389
535
|
const text = await res.text();
|
|
390
536
|
if (text.length > 0) {
|