stitchkit 0.68.3 → 0.68.5
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 -1
- package/dist/agent-runtime/context-refusal.d.ts +13 -0
- package/dist/agent-runtime/context-refusal.d.ts.map +1 -0
- package/dist/agent-runtime/event-schema.d.ts +1341 -0
- package/dist/agent-runtime/event-schema.d.ts.map +1 -0
- package/dist/agent-runtime/events.d.ts +2 -1347
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/run-execution.d.ts +2 -0
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts +6 -0
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +4 -6
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime-browser.d.ts +4 -0
- package/dist/agent-runtime-browser.d.ts.map +1 -0
- package/dist/agent-runtime-browser.js +420 -0
- package/dist/agent-runtime.d.ts +1 -0
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +149 -97
- package/dist/browser/cancellation.d.ts.map +1 -1
- package/dist/browser/contract-stream.d.ts.map +1 -1
- package/dist/browser/stream.d.ts +3 -0
- package/dist/browser/stream.d.ts.map +1 -1
- package/dist/cli.js +5 -5
- package/dist/contract/define.d.ts +39 -6
- 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-cz32amcb.js → index-04agqrs8.js} +2 -2
- package/dist/{index-cszjsxy5.js → index-1bmpkhj2.js} +3 -3
- package/dist/{index-wqyrvhzz.js → index-3ydx9j01.js} +2 -2
- package/dist/{index-69m2278y.js → index-51a19y3v.js} +1 -1
- package/dist/{index-3vq6we8m.js → index-6tqys26z.js} +16 -6
- package/dist/{index-7etq650s.js → index-88yyydag.js} +1 -1
- package/dist/{index-g2j2m6vv.js → index-9t2tdk1x.js} +2 -2
- package/dist/{index-9ky9hhg3.js → index-bmmtz6r9.js} +14 -0
- package/dist/{index-dafax5md.js → index-hmfpjnh7.js} +1 -1
- package/dist/{index-cq9q73nf.js → index-j2nq04z6.js} +14 -0
- package/dist/{index-2ve29dzn.js → index-n34x0q5e.js} +49 -26
- package/dist/{index-v5bayx1z.js → index-r5s4wqb5.js} +7 -3
- package/dist/{index-pr0qsmjy.js → index-z575awm9.js} +1 -1
- package/dist/index.js +54 -27
- package/dist/internal/bounded-lines.d.ts +1 -1
- package/dist/internal/bounded-lines.d.ts.map +1 -1
- package/dist/node.js +4 -4
- package/dist/observability/index.js +3 -3
- package/dist/remote.js +3 -3
- package/dist/server/contract-stream.d.ts.map +1 -1
- package/dist/server/index.js +10 -7
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/testing/agent-store-conformance.d.ts.map +1 -1
- package/dist/testing.js +29 -7
- package/dist/tool-invoker.js +5 -5
- package/dist/tools.js +9 -9
- package/llms-full.txt +119 -8
- package/package.json +6 -2
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
contractToolMountable,
|
|
3
3
|
createToolRunner,
|
|
4
4
|
formatToolError
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-88yyydag.js";
|
|
6
6
|
import {
|
|
7
7
|
coerceJsonArgs,
|
|
8
8
|
toolResultFromError
|
|
9
|
-
} from "./index-
|
|
9
|
+
} from "./index-04agqrs8.js";
|
|
10
10
|
import {
|
|
11
11
|
isWithinDir
|
|
12
12
|
} from "./index-sbdmyz75.js";
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
isUnsafeKey,
|
|
28
28
|
safeJsonParse,
|
|
29
29
|
validateDeclaredOutput
|
|
30
|
-
} from "./index-
|
|
30
|
+
} from "./index-bmmtz6r9.js";
|
|
31
31
|
import {
|
|
32
32
|
isRecord
|
|
33
33
|
} from "./index-smpbdg6k.js";
|
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
contextContributionDelta,
|
|
3
3
|
mergeContextContribution,
|
|
4
4
|
prepareContextContribution
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-z575awm9.js";
|
|
6
6
|
import {
|
|
7
7
|
base64UrlToBytes,
|
|
8
8
|
bytesToBase64Url,
|
|
9
9
|
isUnsafeKey,
|
|
10
10
|
safeJsonParse
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-bmmtz6r9.js";
|
|
12
12
|
import {
|
|
13
13
|
isRecord
|
|
14
14
|
} from "./index-smpbdg6k.js";
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
defaultSignalSource,
|
|
8
8
|
guardSignalCallback,
|
|
9
9
|
reportSignalError
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-z575awm9.js";
|
|
11
11
|
import {
|
|
12
12
|
extractIp,
|
|
13
13
|
getClientInfo,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
runWithRequestContext,
|
|
19
19
|
setRequestEndpoint,
|
|
20
20
|
setRequestError
|
|
21
|
-
} from "./index-
|
|
21
|
+
} from "./index-51a19y3v.js";
|
|
22
22
|
import {
|
|
23
23
|
ShutdownOptionsSchema
|
|
24
24
|
} from "./index-dk6e56g0.js";
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
safeJsonParse,
|
|
35
35
|
validateDeclaredOutput,
|
|
36
36
|
zodIssues
|
|
37
|
-
} from "./index-
|
|
37
|
+
} from "./index-bmmtz6r9.js";
|
|
38
38
|
import {
|
|
39
39
|
isRecord,
|
|
40
40
|
transportResult
|
|
@@ -850,29 +850,39 @@ function contractStreamResponse(request, context, source, descriptor, operationA
|
|
|
850
850
|
throw new AppError("STREAM_ITEM_INVALID", "Stream item did not match its contract", 500);
|
|
851
851
|
}
|
|
852
852
|
const item = parsed.data;
|
|
853
|
-
|
|
853
|
+
const terminal = descriptor.terminal?.safeParse(item).success ?? false;
|
|
854
|
+
if (terminal)
|
|
854
855
|
terminalSeen = true;
|
|
855
|
-
const frame = { type: "data", data: item };
|
|
856
|
+
const frame = descriptor.framing === "item" ? item : { type: "data", data: item };
|
|
856
857
|
if (encodedBytes(frame, format) > maxFrameBytes) {
|
|
857
858
|
console.error(`[stitchkit] contract stream frame exceeded its ${maxFrameBytes} byte limit`);
|
|
858
859
|
throw new AppError("STREAM_FRAME_TOO_LARGE", "Stream item exceeded its declared frame limit", 500);
|
|
859
860
|
}
|
|
860
861
|
yield frame;
|
|
862
|
+
if (terminal && descriptor.completion === "terminal")
|
|
863
|
+
return;
|
|
861
864
|
}
|
|
862
865
|
if (!terminalSeen) {
|
|
863
866
|
throw new AppError("STREAM_TERMINAL_MISSING", "Stream completed before its declared terminal item", 500);
|
|
864
867
|
}
|
|
865
|
-
|
|
868
|
+
if (descriptor.framing !== "item")
|
|
869
|
+
yield { type: "end" };
|
|
866
870
|
} catch (error) {
|
|
867
871
|
if (operationAbort.signal.aborted && !lifetimeExpired)
|
|
868
872
|
return;
|
|
869
873
|
if (lifetimeExpired) {
|
|
874
|
+
if (descriptor.framing === "item")
|
|
875
|
+
return;
|
|
870
876
|
yield {
|
|
871
877
|
type: "error",
|
|
872
878
|
error: new AppError("STREAM_LIFETIME_EXCEEDED", "Stream exceeded its declared lifetime", 408).toJSON().error
|
|
873
879
|
};
|
|
874
880
|
return;
|
|
875
881
|
}
|
|
882
|
+
if (descriptor.framing === "item") {
|
|
883
|
+
console.error("[stitchkit] item-framed contract stream failed:", normalizeError(error));
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
876
886
|
yield { type: "error", error: normalizeError(error).toJSON().error };
|
|
877
887
|
} finally {
|
|
878
888
|
if (lifetime !== undefined)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
collectTools,
|
|
3
3
|
createToolRunner
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-88yyydag.js";
|
|
5
5
|
import {
|
|
6
6
|
toolErrorFromResult
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-04agqrs8.js";
|
|
8
8
|
import {
|
|
9
9
|
assertUniqueToolName
|
|
10
10
|
} from "./index-vkk06pv1.js";
|
|
@@ -177,6 +177,20 @@ function assertStreamingResponseEndpoint(prefix, key, ep) {
|
|
|
177
177
|
if (!ep.stream.item || typeof ep.stream.item.parse !== "function") {
|
|
178
178
|
throw new Error(`${where} must declare an item schema`);
|
|
179
179
|
}
|
|
180
|
+
const runtimeFormat = Reflect.get(ep.stream, "format");
|
|
181
|
+
const runtimeFinalLine = Reflect.get(ep.stream, "finalLine");
|
|
182
|
+
if (ep.stream.framing === "item" && runtimeFormat === "sse") {
|
|
183
|
+
throw new Error(`${where} item framing is supported only for ndjson`);
|
|
184
|
+
}
|
|
185
|
+
if (ep.stream.framing === "item" && ep.stream.completion !== "terminal") {
|
|
186
|
+
throw new Error(`${where} item framing requires terminal completion`);
|
|
187
|
+
}
|
|
188
|
+
if (ep.stream.completion === "terminal" && !ep.stream.terminal) {
|
|
189
|
+
throw new Error(`${where} terminal completion requires a terminal schema`);
|
|
190
|
+
}
|
|
191
|
+
if (runtimeFinalLine === "require-newline" && runtimeFormat === "sse") {
|
|
192
|
+
throw new Error(`${where} finalLine applies only to ndjson`);
|
|
193
|
+
}
|
|
180
194
|
assertPositiveLimit(where, "maxFrameBytes", ep.stream.maxFrameBytes);
|
|
181
195
|
assertPositiveLimit(where, "lifetimeMs", ep.stream.lifetimeMs);
|
|
182
196
|
assertPositiveLimit(where, "heartbeatMs", ep.stream.heartbeatMs);
|
|
@@ -168,6 +168,20 @@ function assertStreamingResponseEndpoint(prefix, key, ep) {
|
|
|
168
168
|
if (!ep.stream.item || typeof ep.stream.item.parse !== "function") {
|
|
169
169
|
throw new Error(`${where} must declare an item schema`);
|
|
170
170
|
}
|
|
171
|
+
const runtimeFormat = Reflect.get(ep.stream, "format");
|
|
172
|
+
const runtimeFinalLine = Reflect.get(ep.stream, "finalLine");
|
|
173
|
+
if (ep.stream.framing === "item" && runtimeFormat === "sse") {
|
|
174
|
+
throw new Error(`${where} item framing is supported only for ndjson`);
|
|
175
|
+
}
|
|
176
|
+
if (ep.stream.framing === "item" && ep.stream.completion !== "terminal") {
|
|
177
|
+
throw new Error(`${where} item framing requires terminal completion`);
|
|
178
|
+
}
|
|
179
|
+
if (ep.stream.completion === "terminal" && !ep.stream.terminal) {
|
|
180
|
+
throw new Error(`${where} terminal completion requires a terminal schema`);
|
|
181
|
+
}
|
|
182
|
+
if (runtimeFinalLine === "require-newline" && runtimeFormat === "sse") {
|
|
183
|
+
throw new Error(`${where} finalLine applies only to ndjson`);
|
|
184
|
+
}
|
|
171
185
|
assertPositiveLimit(where, "maxFrameBytes", ep.stream.maxFrameBytes);
|
|
172
186
|
assertPositiveLimit(where, "lifetimeMs", ep.stream.lifetimeMs);
|
|
173
187
|
assertPositiveLimit(where, "heartbeatMs", ep.stream.heartbeatMs);
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
inputIsQuery,
|
|
4
4
|
parseSSE,
|
|
5
5
|
readBoundedUtf8Lines
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-r5s4wqb5.js";
|
|
7
7
|
import {
|
|
8
8
|
ContractStreamFrameSchema,
|
|
9
9
|
DEFAULT_CONTRACT_STREAM_FRAME_BYTES,
|
|
10
10
|
parseTrailingWildcard
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-bmmtz6r9.js";
|
|
12
12
|
import {
|
|
13
13
|
isRecord,
|
|
14
14
|
mapObject,
|
|
@@ -28,34 +28,34 @@ function createRequestCancellation(caller, timeoutMs) {
|
|
|
28
28
|
if (!caller && timeoutMs === undefined) {
|
|
29
29
|
return { signal: undefined, run: (operation) => operation() };
|
|
30
30
|
}
|
|
31
|
-
const
|
|
31
|
+
const timeoutController = timeoutMs === undefined ? undefined : new AbortController;
|
|
32
|
+
const signal = caller && timeoutController ? AbortSignal.any([caller, timeoutController.signal]) : caller ?? timeoutController?.signal;
|
|
32
33
|
let cause;
|
|
33
34
|
let timer;
|
|
34
35
|
const abortFromCaller = () => {
|
|
35
36
|
if (cause)
|
|
36
37
|
return;
|
|
37
38
|
cause = "caller";
|
|
38
|
-
controller.abort(caller?.reason);
|
|
39
39
|
};
|
|
40
40
|
if (caller?.aborted)
|
|
41
41
|
abortFromCaller();
|
|
42
42
|
else
|
|
43
43
|
caller?.addEventListener("abort", abortFromCaller, { once: true });
|
|
44
|
-
if (timeoutMs !== undefined) {
|
|
44
|
+
if (timeoutMs !== undefined && timeoutController) {
|
|
45
45
|
timer = setTimeout(() => {
|
|
46
46
|
if (cause)
|
|
47
47
|
return;
|
|
48
48
|
cause = "timeout";
|
|
49
|
-
|
|
49
|
+
timeoutController.abort(new DOMException("Request timed out", "TimeoutError"));
|
|
50
50
|
}, timeoutMs);
|
|
51
51
|
}
|
|
52
52
|
return {
|
|
53
|
-
signal
|
|
53
|
+
signal,
|
|
54
54
|
async run(operation) {
|
|
55
55
|
try {
|
|
56
56
|
if (cause === "caller")
|
|
57
57
|
throw cancellationError(cause);
|
|
58
|
-
return await operation(
|
|
58
|
+
return await operation(signal);
|
|
59
59
|
} catch (error) {
|
|
60
60
|
if (cause)
|
|
61
61
|
throw cancellationError(cause);
|
|
@@ -291,7 +291,7 @@ function parseFailure(raw, error, onParseError) {
|
|
|
291
291
|
}
|
|
292
292
|
async function* parseNDJSON(response, options) {
|
|
293
293
|
try {
|
|
294
|
-
for await (const rawLine of readBoundedUtf8Lines(response, options?.maxLineBytes ?? DEFAULT_STREAM_LINE_BYTES)) {
|
|
294
|
+
for await (const rawLine of readBoundedUtf8Lines(response, options?.maxLineBytes ?? DEFAULT_STREAM_LINE_BYTES, options?.finalLine === "require-newline")) {
|
|
295
295
|
const line = (rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine).trim();
|
|
296
296
|
if (line === "")
|
|
297
297
|
continue;
|
|
@@ -309,44 +309,67 @@ async function* parseNDJSON(response, options) {
|
|
|
309
309
|
// src/browser/contract-stream.ts
|
|
310
310
|
function sourceFor(response, descriptor) {
|
|
311
311
|
const options = {
|
|
312
|
-
maxLineBytes: descriptor.maxFrameBytes ?? DEFAULT_CONTRACT_STREAM_FRAME_BYTES
|
|
312
|
+
maxLineBytes: descriptor.maxFrameBytes ?? DEFAULT_CONTRACT_STREAM_FRAME_BYTES,
|
|
313
|
+
finalLine: descriptor.finalLine
|
|
313
314
|
};
|
|
314
315
|
return descriptor.format === "sse" ? parseSSE(response, options) : parseNDJSON(response, options);
|
|
315
316
|
}
|
|
316
317
|
async function* readContractStream(response, descriptor, abort) {
|
|
317
318
|
let ended = false;
|
|
318
319
|
let terminalSeen = descriptor.terminal === undefined;
|
|
320
|
+
const source = sourceFor(response, descriptor);
|
|
319
321
|
try {
|
|
320
|
-
for
|
|
321
|
-
const
|
|
322
|
-
if (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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");
|
|
322
|
+
for (;; ) {
|
|
323
|
+
const next = await source.next();
|
|
324
|
+
if (next.done)
|
|
325
|
+
break;
|
|
326
|
+
let candidate = next.value;
|
|
327
|
+
if (descriptor.framing !== "item") {
|
|
328
|
+
const parsedFrame = ContractStreamFrameSchema.safeParse(candidate);
|
|
329
|
+
if (!parsedFrame.success) {
|
|
330
|
+
throw new ApiError("STREAM_PROTOCOL_ERROR", 0, undefined, "Stream frame did not match the protocol envelope");
|
|
332
331
|
}
|
|
333
|
-
|
|
334
|
-
|
|
332
|
+
const frame = parsedFrame.data;
|
|
333
|
+
if (frame.type === "error") {
|
|
334
|
+
throw new ApiError(frame.error.code, 0, frame.error.details, frame.error.message, frame.error.hint);
|
|
335
|
+
}
|
|
336
|
+
if (frame.type === "end") {
|
|
337
|
+
if (!terminalSeen) {
|
|
338
|
+
throw new ApiError("STREAM_TERMINAL_MISSING", 0, undefined, "Stream completed before its declared terminal item");
|
|
339
|
+
}
|
|
340
|
+
ended = true;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
candidate = frame.data;
|
|
335
344
|
}
|
|
336
|
-
const parsedItem = descriptor.item.safeParse(
|
|
345
|
+
const parsedItem = descriptor.item.safeParse(candidate);
|
|
337
346
|
if (!parsedItem.success) {
|
|
338
347
|
throw new ApiError("STREAM_ITEM_INVALID", 0, undefined, "Stream item did not match its contract");
|
|
339
348
|
}
|
|
340
349
|
const item = parsedItem.data;
|
|
341
|
-
|
|
350
|
+
const terminal = descriptor.terminal?.safeParse(item).success ?? false;
|
|
351
|
+
if (terminal)
|
|
342
352
|
terminalSeen = true;
|
|
353
|
+
if (terminal && descriptor.completion === "terminal") {
|
|
354
|
+
abort();
|
|
355
|
+
await source.return(undefined);
|
|
356
|
+
ended = true;
|
|
357
|
+
}
|
|
343
358
|
yield item;
|
|
359
|
+
if (ended)
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (descriptor.framing === "item") {
|
|
363
|
+
if (terminalSeen)
|
|
364
|
+
return;
|
|
365
|
+
throw new ApiError("STREAM_TERMINAL_MISSING", 0, undefined, "Stream completed before its declared terminal item");
|
|
344
366
|
}
|
|
345
367
|
if (!ended) {
|
|
346
368
|
throw new ApiError("STREAM_TRUNCATED", 0, undefined, "Stream ended without its protocol terminal frame");
|
|
347
369
|
}
|
|
348
370
|
} finally {
|
|
349
371
|
abort();
|
|
372
|
+
await source.return(undefined);
|
|
350
373
|
}
|
|
351
374
|
}
|
|
352
375
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
normalizeError
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-bmmtz6r9.js";
|
|
4
4
|
|
|
5
5
|
// src/internal/bounded-lines.ts
|
|
6
6
|
var DEFAULT_STREAM_LINE_BYTES = 1024 * 1024;
|
|
@@ -19,7 +19,7 @@ function joinBytes(left, right) {
|
|
|
19
19
|
result.set(right, left.byteLength);
|
|
20
20
|
return result;
|
|
21
21
|
}
|
|
22
|
-
async function* readBoundedUtf8Lines(response, maxLineBytes) {
|
|
22
|
+
async function* readBoundedUtf8Lines(response, maxLineBytes, requireFinalNewline = false) {
|
|
23
23
|
const reader = response.body?.getReader();
|
|
24
24
|
if (!reader)
|
|
25
25
|
return;
|
|
@@ -48,8 +48,12 @@ async function* readBoundedUtf8Lines(response, maxLineBytes) {
|
|
|
48
48
|
throw new RangeError(`Stream line exceeds the ${limit} byte limit`);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
if (pending.byteLength > 0)
|
|
51
|
+
if (pending.byteLength > 0) {
|
|
52
|
+
if (requireFinalNewline) {
|
|
53
|
+
throw new SyntaxError("Stream ended with an unterminated final line");
|
|
54
|
+
}
|
|
52
55
|
yield decoder.decode(pending);
|
|
56
|
+
}
|
|
53
57
|
} finally {
|
|
54
58
|
await reader.cancel().catch(() => {
|
|
55
59
|
return;
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
parseTrailingWildcard,
|
|
22
22
|
rateLimited,
|
|
23
23
|
unauthorized
|
|
24
|
-
} from "./index-
|
|
24
|
+
} from "./index-j2nq04z6.js";
|
|
25
25
|
import {
|
|
26
26
|
isRecord,
|
|
27
27
|
mapObject,
|
|
@@ -42,34 +42,34 @@ function createRequestCancellation(caller, timeoutMs) {
|
|
|
42
42
|
if (!caller && timeoutMs === undefined) {
|
|
43
43
|
return { signal: undefined, run: (operation) => operation() };
|
|
44
44
|
}
|
|
45
|
-
const
|
|
45
|
+
const timeoutController = timeoutMs === undefined ? undefined : new AbortController;
|
|
46
|
+
const signal = caller && timeoutController ? AbortSignal.any([caller, timeoutController.signal]) : caller ?? timeoutController?.signal;
|
|
46
47
|
let cause;
|
|
47
48
|
let timer;
|
|
48
49
|
const abortFromCaller = () => {
|
|
49
50
|
if (cause)
|
|
50
51
|
return;
|
|
51
52
|
cause = "caller";
|
|
52
|
-
controller.abort(caller?.reason);
|
|
53
53
|
};
|
|
54
54
|
if (caller?.aborted)
|
|
55
55
|
abortFromCaller();
|
|
56
56
|
else
|
|
57
57
|
caller?.addEventListener("abort", abortFromCaller, { once: true });
|
|
58
|
-
if (timeoutMs !== undefined) {
|
|
58
|
+
if (timeoutMs !== undefined && timeoutController) {
|
|
59
59
|
timer = setTimeout(() => {
|
|
60
60
|
if (cause)
|
|
61
61
|
return;
|
|
62
62
|
cause = "timeout";
|
|
63
|
-
|
|
63
|
+
timeoutController.abort(new DOMException("Request timed out", "TimeoutError"));
|
|
64
64
|
}, timeoutMs);
|
|
65
65
|
}
|
|
66
66
|
return {
|
|
67
|
-
signal
|
|
67
|
+
signal,
|
|
68
68
|
async run(operation) {
|
|
69
69
|
try {
|
|
70
70
|
if (cause === "caller")
|
|
71
71
|
throw cancellationError(cause);
|
|
72
|
-
return await operation(
|
|
72
|
+
return await operation(signal);
|
|
73
73
|
} catch (error) {
|
|
74
74
|
if (cause)
|
|
75
75
|
throw cancellationError(cause);
|
|
@@ -315,7 +315,7 @@ function joinBytes(left, right) {
|
|
|
315
315
|
result.set(right, left.byteLength);
|
|
316
316
|
return result;
|
|
317
317
|
}
|
|
318
|
-
async function* readBoundedUtf8Lines(response, maxLineBytes) {
|
|
318
|
+
async function* readBoundedUtf8Lines(response, maxLineBytes, requireFinalNewline = false) {
|
|
319
319
|
const reader = response.body?.getReader();
|
|
320
320
|
if (!reader)
|
|
321
321
|
return;
|
|
@@ -344,8 +344,12 @@ async function* readBoundedUtf8Lines(response, maxLineBytes) {
|
|
|
344
344
|
throw new RangeError(`Stream line exceeds the ${limit} byte limit`);
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
|
-
if (pending.byteLength > 0)
|
|
347
|
+
if (pending.byteLength > 0) {
|
|
348
|
+
if (requireFinalNewline) {
|
|
349
|
+
throw new SyntaxError("Stream ended with an unterminated final line");
|
|
350
|
+
}
|
|
348
351
|
yield decoder.decode(pending);
|
|
352
|
+
}
|
|
349
353
|
} finally {
|
|
350
354
|
await reader.cancel().catch(() => {
|
|
351
355
|
return;
|
|
@@ -701,7 +705,7 @@ function parseFailure(raw, error, onParseError) {
|
|
|
701
705
|
}
|
|
702
706
|
async function* parseNDJSON(response, options) {
|
|
703
707
|
try {
|
|
704
|
-
for await (const rawLine of readBoundedUtf8Lines(response, options?.maxLineBytes ?? DEFAULT_STREAM_LINE_BYTES)) {
|
|
708
|
+
for await (const rawLine of readBoundedUtf8Lines(response, options?.maxLineBytes ?? DEFAULT_STREAM_LINE_BYTES, options?.finalLine === "require-newline")) {
|
|
705
709
|
const line = (rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine).trim();
|
|
706
710
|
if (line === "")
|
|
707
711
|
continue;
|
|
@@ -719,44 +723,67 @@ async function* parseNDJSON(response, options) {
|
|
|
719
723
|
// src/browser/contract-stream.ts
|
|
720
724
|
function sourceFor(response, descriptor) {
|
|
721
725
|
const options = {
|
|
722
|
-
maxLineBytes: descriptor.maxFrameBytes ?? DEFAULT_CONTRACT_STREAM_FRAME_BYTES
|
|
726
|
+
maxLineBytes: descriptor.maxFrameBytes ?? DEFAULT_CONTRACT_STREAM_FRAME_BYTES,
|
|
727
|
+
finalLine: descriptor.finalLine
|
|
723
728
|
};
|
|
724
729
|
return descriptor.format === "sse" ? parseSSE(response, options) : parseNDJSON(response, options);
|
|
725
730
|
}
|
|
726
731
|
async function* readContractStream(response, descriptor, abort) {
|
|
727
732
|
let ended = false;
|
|
728
733
|
let terminalSeen = descriptor.terminal === undefined;
|
|
734
|
+
const source = sourceFor(response, descriptor);
|
|
729
735
|
try {
|
|
730
|
-
for
|
|
731
|
-
const
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
if (frame.type === "end") {
|
|
740
|
-
if (!terminalSeen) {
|
|
741
|
-
throw new ApiError("STREAM_TERMINAL_MISSING", 0, undefined, "Stream completed before its declared terminal item");
|
|
736
|
+
for (;; ) {
|
|
737
|
+
const next = await source.next();
|
|
738
|
+
if (next.done)
|
|
739
|
+
break;
|
|
740
|
+
let candidate = next.value;
|
|
741
|
+
if (descriptor.framing !== "item") {
|
|
742
|
+
const parsedFrame = ContractStreamFrameSchema.safeParse(candidate);
|
|
743
|
+
if (!parsedFrame.success) {
|
|
744
|
+
throw new ApiError("STREAM_PROTOCOL_ERROR", 0, undefined, "Stream frame did not match the protocol envelope");
|
|
742
745
|
}
|
|
743
|
-
|
|
744
|
-
|
|
746
|
+
const frame = parsedFrame.data;
|
|
747
|
+
if (frame.type === "error") {
|
|
748
|
+
throw new ApiError(frame.error.code, 0, frame.error.details, frame.error.message, frame.error.hint);
|
|
749
|
+
}
|
|
750
|
+
if (frame.type === "end") {
|
|
751
|
+
if (!terminalSeen) {
|
|
752
|
+
throw new ApiError("STREAM_TERMINAL_MISSING", 0, undefined, "Stream completed before its declared terminal item");
|
|
753
|
+
}
|
|
754
|
+
ended = true;
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
candidate = frame.data;
|
|
745
758
|
}
|
|
746
|
-
const parsedItem = descriptor.item.safeParse(
|
|
759
|
+
const parsedItem = descriptor.item.safeParse(candidate);
|
|
747
760
|
if (!parsedItem.success) {
|
|
748
761
|
throw new ApiError("STREAM_ITEM_INVALID", 0, undefined, "Stream item did not match its contract");
|
|
749
762
|
}
|
|
750
763
|
const item = parsedItem.data;
|
|
751
|
-
|
|
764
|
+
const terminal = descriptor.terminal?.safeParse(item).success ?? false;
|
|
765
|
+
if (terminal)
|
|
752
766
|
terminalSeen = true;
|
|
767
|
+
if (terminal && descriptor.completion === "terminal") {
|
|
768
|
+
abort();
|
|
769
|
+
await source.return(undefined);
|
|
770
|
+
ended = true;
|
|
771
|
+
}
|
|
753
772
|
yield item;
|
|
773
|
+
if (ended)
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (descriptor.framing === "item") {
|
|
777
|
+
if (terminalSeen)
|
|
778
|
+
return;
|
|
779
|
+
throw new ApiError("STREAM_TERMINAL_MISSING", 0, undefined, "Stream completed before its declared terminal item");
|
|
754
780
|
}
|
|
755
781
|
if (!ended) {
|
|
756
782
|
throw new ApiError("STREAM_TRUNCATED", 0, undefined, "Stream ended without its protocol terminal frame");
|
|
757
783
|
}
|
|
758
784
|
} finally {
|
|
759
785
|
abort();
|
|
786
|
+
await source.return(undefined);
|
|
760
787
|
}
|
|
761
788
|
}
|
|
762
789
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const DEFAULT_STREAM_LINE_BYTES: number;
|
|
2
2
|
/** Bounded, fatal-UTF-8 line reader shared by NDJSON and SSE parsers. */
|
|
3
|
-
export declare function readBoundedUtf8Lines(response: Response, maxLineBytes?: number): AsyncGenerator<string>;
|
|
3
|
+
export declare function readBoundedUtf8Lines(response: Response, maxLineBytes?: number, requireFinalNewline?: boolean): AsyncGenerator<string>;
|
|
4
4
|
//# sourceMappingURL=bounded-lines.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounded-lines.d.ts","sourceRoot":"","sources":["../../src/internal/bounded-lines.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,QAAc,CAAC;AAkBrD,yEAAyE;AACzE,wBAAuB,oBAAoB,CACzC,QAAQ,EAAE,QAAQ,EAClB,YAAY,CAAC,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"bounded-lines.d.ts","sourceRoot":"","sources":["../../src/internal/bounded-lines.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,QAAc,CAAC;AAkBrD,yEAAyE;AACzE,wBAAuB,oBAAoB,CACzC,QAAQ,EAAE,QAAQ,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,mBAAmB,UAAQ,GAC1B,cAAc,CAAC,MAAM,CAAC,CAqCxB"}
|
package/dist/node.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
createHandler,
|
|
6
6
|
createSocketIOServer,
|
|
7
7
|
createUnixClientTransport
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-6tqys26z.js";
|
|
9
9
|
import {
|
|
10
10
|
createImplement,
|
|
11
11
|
createImplementRegistry,
|
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
createScopedImplementRegistry,
|
|
15
15
|
implement,
|
|
16
16
|
implementRegistry
|
|
17
|
-
} from "./index-
|
|
18
|
-
import"./index-
|
|
17
|
+
} from "./index-z575awm9.js";
|
|
18
|
+
import"./index-51a19y3v.js";
|
|
19
19
|
import"./index-557by2db.js";
|
|
20
20
|
import {
|
|
21
21
|
ShutdownOptionsSchema,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
ShutdownStatusSchema,
|
|
25
25
|
createServerLifecycle
|
|
26
26
|
} from "./index-dk6e56g0.js";
|
|
27
|
-
import"./index-
|
|
27
|
+
import"./index-bmmtz6r9.js";
|
|
28
28
|
import"./index-smpbdg6k.js";
|
|
29
29
|
import {
|
|
30
30
|
AppError,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
redact,
|
|
11
11
|
sanitizePayload,
|
|
12
12
|
truncatePreview
|
|
13
|
-
} from "../index-
|
|
13
|
+
} from "../index-hmfpjnh7.js";
|
|
14
14
|
import {
|
|
15
15
|
getRequestContext,
|
|
16
16
|
getTraceId,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
setRequestError,
|
|
22
22
|
setRequestUser,
|
|
23
23
|
wrapInRequestContext
|
|
24
|
-
} from "../index-
|
|
24
|
+
} from "../index-51a19y3v.js";
|
|
25
25
|
import {
|
|
26
26
|
childSpan,
|
|
27
27
|
createTraceContext,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
recordedErrorMessage,
|
|
31
31
|
resolvePropagationContext,
|
|
32
32
|
resolveTraceContext
|
|
33
|
-
} from "../index-
|
|
33
|
+
} from "../index-bmmtz6r9.js";
|
|
34
34
|
import {
|
|
35
35
|
isRecord
|
|
36
36
|
} from "../index-smpbdg6k.js";
|
package/dist/remote.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ApiError,
|
|
3
3
|
createClient
|
|
4
|
-
} from "./index-
|
|
5
|
-
import"./index-
|
|
4
|
+
} from "./index-n34x0q5e.js";
|
|
5
|
+
import"./index-r5s4wqb5.js";
|
|
6
6
|
import {
|
|
7
7
|
mergeMeta
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-bmmtz6r9.js";
|
|
9
9
|
import {
|
|
10
10
|
isRecord
|
|
11
11
|
} from "./index-smpbdg6k.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-stream.d.ts","sourceRoot":"","sources":["../../src/server/contract-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAiB/C,gEAAgE;AAChE,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAC9B,UAAU,EAAE,wBAAwB,EACpC,cAAc,EAAE,eAAe,GAC9B,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"contract-stream.d.ts","sourceRoot":"","sources":["../../src/server/contract-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAiB/C,gEAAgE;AAChE,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAC9B,UAAU,EAAE,wBAAwB,EACpC,cAAc,EAAE,eAAe,GAC9B,OAAO,CAAC,QAAQ,CAAC,CA6FnB"}
|