stitchkit 0.110.5 → 0.111.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/CHANGELOG.md +43 -0
- package/dist/agent-runtime/branch-observations.d.ts +16 -16
- package/dist/agent-runtime/sandbox-broker.d.ts.map +1 -1
- package/dist/browser/stream.d.ts +3 -2
- package/dist/browser/stream.d.ts.map +1 -1
- package/dist/contract/runtime-context.d.ts +3 -1
- package/dist/contract/runtime-context.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/coding-tools.js +2 -2
- package/dist/entrypoints/agent-runtime/sandbox.js +15 -29
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +4 -4
- package/dist/entrypoints/agent-runtime/sqlite/node.js +4 -4
- package/dist/entrypoints/agent-runtime/testing.js +4 -4
- package/dist/entrypoints/agent-runtime.js +4 -4
- package/dist/entrypoints/bin/upgrade-cli.d.ts.map +1 -1
- package/dist/entrypoints/bin/upgrade-cli.js +123 -29
- package/dist/entrypoints/cli/publish.js +1 -1
- package/dist/entrypoints/cli.js +2 -2
- package/dist/entrypoints/http.d.ts +2 -0
- package/dist/entrypoints/http.d.ts.map +1 -1
- package/dist/entrypoints/http.js +5 -1
- package/dist/entrypoints/index.d.ts +1 -0
- package/dist/entrypoints/index.d.ts.map +1 -1
- package/dist/entrypoints/index.js +5 -1
- package/dist/entrypoints/node.d.ts +1 -0
- package/dist/entrypoints/node.d.ts.map +1 -1
- package/dist/entrypoints/node.js +3 -1
- package/dist/entrypoints/observability.js +24 -9
- package/dist/entrypoints/process.js +1 -1
- package/dist/entrypoints/remote.js +1 -1
- package/dist/entrypoints/server.d.ts +2 -0
- package/dist/entrypoints/server.d.ts.map +1 -1
- package/dist/entrypoints/server.js +79 -6
- package/dist/entrypoints/telegram.js +3 -3
- package/dist/entrypoints/testing.js +1 -1
- package/dist/entrypoints/tools/connections.js +16 -31
- package/dist/entrypoints/tools.js +39 -7
- package/dist/{index-mpg1t7nv.js → index-0z3kfphz.js} +1 -1
- package/dist/{index-jk44mbqb.js → index-1y0arq4z.js} +3 -57
- package/dist/{index-5pbtbce0.js → index-7c957abz.js} +1 -1
- package/dist/{index-e1sjcjfy.js → index-7r4knwf8.js} +94 -23
- package/dist/index-cjbn98zt.js +68 -0
- package/dist/{index-bm180f2d.js → index-ey15wx6j.js} +1 -1
- package/dist/{index-s0xbnpfh.js → index-kdnfe0sr.js} +1 -1
- package/dist/{index-s6d8zsha.js → index-krng2a5j.js} +64 -110
- package/dist/{index-9ket8wzx.js → index-mnk5vgdr.js} +1 -1
- package/dist/index-qndsr7f0.js +55 -0
- package/dist/{index-n7hrpngg.js → index-syzngt7v.js} +1 -1
- package/dist/{index-3h0ztg0z.js → index-t65erk4z.js} +5 -21
- package/dist/internal/bounded-body.d.ts +44 -0
- package/dist/internal/bounded-body.d.ts.map +1 -0
- package/dist/internal/bounded-lines.d.ts +1 -2
- package/dist/internal/bounded-lines.d.ts.map +1 -1
- package/dist/internal/secure-fetch.d.ts.map +1 -1
- package/dist/internal/stream-chunks.d.ts +13 -0
- package/dist/internal/stream-chunks.d.ts.map +1 -0
- package/dist/internal/upgrade-affects.d.ts +5 -0
- package/dist/internal/upgrade-affects.d.ts.map +1 -1
- package/dist/internal/upgrade-plan.d.ts +18 -3
- package/dist/internal/upgrade-plan.d.ts.map +1 -1
- package/dist/internal/upgrade-usage.d.ts +21 -5
- package/dist/internal/upgrade-usage.d.ts.map +1 -1
- package/dist/observability/audit.d.ts +10 -0
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/event.d.ts +5 -0
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/process/group-diagnostics.d.ts +20 -0
- package/dist/process/group-diagnostics.d.ts.map +1 -0
- package/dist/process/group-settlement.d.ts +1 -1
- package/dist/process/group-settlement.d.ts.map +1 -1
- package/dist/process/group.d.ts.map +1 -1
- package/dist/server/bun.d.ts +9 -0
- package/dist/server/bun.d.ts.map +1 -1
- package/dist/server/multipart.d.ts.map +1 -1
- package/dist/server/oauth/endpoints.d.ts.map +1 -1
- package/dist/server/oauth/provider.d.ts +5 -0
- package/dist/server/oauth/provider.d.ts.map +1 -1
- package/dist/server/proxy-origin.d.ts +12 -0
- package/dist/server/proxy-origin.d.ts.map +1 -0
- package/dist/server/raw.d.ts +6 -3
- package/dist/server/raw.d.ts.map +1 -1
- package/dist/server/request-body.d.ts.map +1 -1
- package/dist/server/request-completion.d.ts.map +1 -1
- package/dist/server/request-ingress.d.ts +31 -0
- package/dist/server/request-ingress.d.ts.map +1 -0
- package/dist/server/unix-client-body.d.ts.map +1 -1
- package/dist/tools/connections/limits.d.ts.map +1 -1
- package/llms/http.txt +3 -0
- package/llms/observability.txt +19 -2
- package/llms/server.2.txt +135 -0
- package/llms/server.5.txt +3 -1
- package/llms/server.txt +97 -131
- package/llms/stitchkit.2.txt +28 -0
- package/llms/stitchkit.txt +3 -3
- package/llms/tools.3.txt +1 -1
- package/llms/tools.txt +4 -0
- package/llms/{upgrading-0.101.0-0.110.0.txt → upgrading-0.101.0-0.111.0.txt} +29 -3
- package/llms/upgrading.txt +8 -2
- package/llms-full.txt +192 -8
- package/llms.txt +6 -6
- package/package.json +1 -1
|
@@ -14,10 +14,6 @@ import {
|
|
|
14
14
|
withUploadProgress,
|
|
15
15
|
parseApiErrorBody
|
|
16
16
|
} from "./index-p6apg0jg.js";
|
|
17
|
-
import {
|
|
18
|
-
abortReason,
|
|
19
|
-
untilAborted
|
|
20
|
-
} from "./index-a5qr2rj6.js";
|
|
21
17
|
import {
|
|
22
18
|
inputIsQuery
|
|
23
19
|
} from "./index-xhzq2qvj.js";
|
|
@@ -25,6 +21,9 @@ import {
|
|
|
25
21
|
ApiError2,
|
|
26
22
|
refuseLocally
|
|
27
23
|
} from "./index-kfjkb396.js";
|
|
24
|
+
import {
|
|
25
|
+
chunksOf
|
|
26
|
+
} from "./index-cjbn98zt.js";
|
|
28
27
|
import {
|
|
29
28
|
assertPositiveSafeInteger
|
|
30
29
|
} from "./index-0s4xebbg.js";
|
|
@@ -131,59 +130,6 @@ function createBoundedLineDecoder(maxLineBytes, requireFinalNewline = false) {
|
|
|
131
130
|
const { push, end } = createLineSplitter(maxLineBytes, requireFinalNewline);
|
|
132
131
|
return { push, end };
|
|
133
132
|
}
|
|
134
|
-
async function* chunksOf(source, signal) {
|
|
135
|
-
signal?.throwIfAborted();
|
|
136
|
-
const stream = source instanceof Response ? source.body : source;
|
|
137
|
-
if (stream === null)
|
|
138
|
-
return;
|
|
139
|
-
if (stream instanceof ReadableStream) {
|
|
140
|
-
const reader = stream.getReader();
|
|
141
|
-
const abort = () => {
|
|
142
|
-
if (signal)
|
|
143
|
-
reader.cancel(abortReason(signal)).catch(() => {
|
|
144
|
-
return;
|
|
145
|
-
});
|
|
146
|
-
};
|
|
147
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
148
|
-
try {
|
|
149
|
-
for (;; ) {
|
|
150
|
-
const chunk = await reader.read();
|
|
151
|
-
signal?.throwIfAborted();
|
|
152
|
-
if (chunk.done)
|
|
153
|
-
return;
|
|
154
|
-
yield chunk.value;
|
|
155
|
-
}
|
|
156
|
-
} finally {
|
|
157
|
-
signal?.removeEventListener("abort", abort);
|
|
158
|
-
await reader.cancel().catch(() => {
|
|
159
|
-
return;
|
|
160
|
-
});
|
|
161
|
-
reader.releaseLock();
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
const iterator = stream[Symbol.asyncIterator]();
|
|
165
|
-
let finished = false;
|
|
166
|
-
try {
|
|
167
|
-
for (;; ) {
|
|
168
|
-
const pending = iterator.next();
|
|
169
|
-
const next = await (signal ? untilAborted(pending, signal) : pending);
|
|
170
|
-
if (next.done) {
|
|
171
|
-
finished = true;
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
yield next.value;
|
|
175
|
-
}
|
|
176
|
-
} finally {
|
|
177
|
-
if (!finished && signal?.aborted)
|
|
178
|
-
iterator.return?.().catch(() => {
|
|
179
|
-
return;
|
|
180
|
-
});
|
|
181
|
-
else if (!finished)
|
|
182
|
-
await iterator.return?.().catch(() => {
|
|
183
|
-
return;
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
133
|
async function* readBoundedUtf8Lines(source, maxLineBytes, requireFinalNewline = false, signal) {
|
|
188
134
|
const splitter = createLineSplitter(maxLineBytes, requireFinalNewline);
|
|
189
135
|
for await (const chunk of chunksOf(source, signal)) {
|
|
@@ -23,6 +23,62 @@ import {
|
|
|
23
23
|
assertPositiveSafeInteger
|
|
24
24
|
} from "./index-0s4xebbg.js";
|
|
25
25
|
|
|
26
|
+
// src/process/group-diagnostics.ts
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
var ObservationSchema = z.object({
|
|
29
|
+
failure: z.enum(["deadline", "observation"]),
|
|
30
|
+
groupId: z.number().int().positive(),
|
|
31
|
+
observations: z.number().int().nonnegative(),
|
|
32
|
+
lastStates: z.array(z.string().max(32)).max(16),
|
|
33
|
+
elapsedMs: z.number().int().nonnegative()
|
|
34
|
+
});
|
|
35
|
+
var observations = new WeakMap;
|
|
36
|
+
|
|
37
|
+
class GroupSettlementError extends Error {
|
|
38
|
+
constructor(message, observation, cause = observation) {
|
|
39
|
+
super(`${message}: ${JSON.stringify(observation)}`, { cause });
|
|
40
|
+
observations.set(this, { ...observation, lastStates: [...observation.lastStates] });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function ownData(value, key) {
|
|
44
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
45
|
+
return descriptor && "value" in descriptor ? descriptor.value : undefined;
|
|
46
|
+
}
|
|
47
|
+
function groupSettlementDiagnostics(cause) {
|
|
48
|
+
const pending = [{ value: cause, depth: 0 }];
|
|
49
|
+
const seen = new Set;
|
|
50
|
+
const found = [];
|
|
51
|
+
for (let visited = 0;pending.length && visited < 32 && found.length < 4; visited++) {
|
|
52
|
+
const entry = pending.pop();
|
|
53
|
+
if (!entry || seen.has(entry.value))
|
|
54
|
+
continue;
|
|
55
|
+
seen.add(entry.value);
|
|
56
|
+
try {
|
|
57
|
+
if (entry.value instanceof Error) {
|
|
58
|
+
const observation = observations.get(entry.value);
|
|
59
|
+
if (observation)
|
|
60
|
+
found.push(observation);
|
|
61
|
+
}
|
|
62
|
+
if (!(entry.value instanceof Error) || entry.depth >= 8)
|
|
63
|
+
continue;
|
|
64
|
+
if (entry.value instanceof AggregateError) {
|
|
65
|
+
const errors = ownData(entry.value, "errors");
|
|
66
|
+
if (Array.isArray(errors)) {
|
|
67
|
+
for (let index = 0;index < 16; index++) {
|
|
68
|
+
const value = ownData(errors, String(index));
|
|
69
|
+
if (value !== undefined)
|
|
70
|
+
pending.push({ value, depth: entry.depth + 1 });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const nested = ownData(entry.value, "cause");
|
|
75
|
+
if (nested !== undefined)
|
|
76
|
+
pending.push({ value: nested, depth: entry.depth + 1 });
|
|
77
|
+
} catch {}
|
|
78
|
+
}
|
|
79
|
+
return found;
|
|
80
|
+
}
|
|
81
|
+
|
|
26
82
|
// src/process/group-presence.ts
|
|
27
83
|
function processGroupExists(pid) {
|
|
28
84
|
try {
|
|
@@ -57,36 +113,49 @@ function processGroups(timeoutMs) {
|
|
|
57
113
|
return new Promise((resolve, reject) => {
|
|
58
114
|
execFile("ps", ["-axo", "pgid=,stat="], { encoding: "utf8", timeout: timeoutMs, killSignal: "SIGKILL", maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
59
115
|
if (error)
|
|
60
|
-
reject(
|
|
116
|
+
reject(error);
|
|
61
117
|
else
|
|
62
118
|
resolve(stdout);
|
|
63
119
|
});
|
|
64
120
|
});
|
|
65
121
|
}
|
|
66
|
-
async function waitForProcessGroupExit(pid, deadline) {
|
|
122
|
+
async function waitForProcessGroupExit(pid, deadline, killLiveMembers) {
|
|
67
123
|
if (pid === undefined)
|
|
68
124
|
return;
|
|
69
125
|
let observations = 0;
|
|
70
126
|
let lastStates = [];
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
127
|
+
const startedAt = performance.now();
|
|
128
|
+
const observation = () => ({
|
|
129
|
+
groupId: pid,
|
|
130
|
+
observations,
|
|
131
|
+
lastStates,
|
|
132
|
+
elapsedMs: Math.max(0, Math.ceil(performance.now() - startedAt))
|
|
133
|
+
});
|
|
134
|
+
try {
|
|
135
|
+
for (;; ) {
|
|
136
|
+
const remaining = Math.ceil(deadline - performance.now());
|
|
137
|
+
if (remaining <= 0)
|
|
138
|
+
throw new GroupSettlementError("Command process group settlement was not confirmed after SIGKILL", { ...observation(), failure: "deadline" });
|
|
139
|
+
if (!processGroupExists(pid))
|
|
140
|
+
return;
|
|
141
|
+
const table = await processGroups(remaining);
|
|
142
|
+
observations++;
|
|
143
|
+
if (!hasLiveGroupMember(table, pid))
|
|
144
|
+
return;
|
|
145
|
+
lastStates = table.split(/\r?\n/).flatMap((row) => {
|
|
146
|
+
const fields = /^\s*(\d+)\s+(\S+)\s*$/.exec(row);
|
|
147
|
+
return Number(fields?.[1]) === pid ? [(fields?.[2] ?? "unknown").slice(0, 32)] : [];
|
|
148
|
+
}).slice(0, 16);
|
|
149
|
+
if (!processGroupExists(pid))
|
|
150
|
+
return;
|
|
151
|
+
if (killLiveMembers && performance.now() < deadline && !await killLiveMembers())
|
|
152
|
+
return;
|
|
153
|
+
await sleep(Math.min(5, Math.max(0, deadline - performance.now())));
|
|
154
|
+
}
|
|
155
|
+
} catch (cause) {
|
|
156
|
+
if (cause instanceof GroupSettlementError)
|
|
157
|
+
throw cause;
|
|
158
|
+
throw new GroupSettlementError("Could not observe command process group settlement", { ...observation(), failure: "observation" }, cause);
|
|
90
159
|
}
|
|
91
160
|
}
|
|
92
161
|
|
|
@@ -151,7 +220,7 @@ async function killAndSettleGroup(pid, deadline, cleanupTimeoutMs) {
|
|
|
151
220
|
const cleanupDeadline = Math.min(deadline, performance.now() + cleanupTimeoutMs);
|
|
152
221
|
const signalled = await signalUntilSettled(pid, "SIGKILL", cleanupDeadline);
|
|
153
222
|
if (signalled)
|
|
154
|
-
await waitForProcessGroupExit(pid, cleanupDeadline);
|
|
223
|
+
await waitForProcessGroupExit(pid, cleanupDeadline, () => signalUntilSettled(pid, "SIGKILL", cleanupDeadline));
|
|
155
224
|
return signalled;
|
|
156
225
|
}
|
|
157
226
|
async function stopCommandGroup(stop) {
|
|
@@ -235,7 +304,9 @@ async function stopLeaderAlone(stop) {
|
|
|
235
304
|
return send("SIGKILL") || signalled;
|
|
236
305
|
}
|
|
237
306
|
function commandCleanupError(cause) {
|
|
238
|
-
|
|
307
|
+
const observations = groupSettlementDiagnostics(cause);
|
|
308
|
+
const message = observations.length ? `Command cleanup did not complete: ${JSON.stringify(observations)}` : "Command cleanup did not complete";
|
|
309
|
+
return new NativeCommandError2("COMMAND_CLEANUP", message, {
|
|
239
310
|
cause
|
|
240
311
|
});
|
|
241
312
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
abortReason,
|
|
3
|
+
untilAborted
|
|
4
|
+
} from "./index-a5qr2rj6.js";
|
|
5
|
+
|
|
6
|
+
// src/internal/stream-chunks.ts
|
|
7
|
+
async function* chunksOf(source, signal) {
|
|
8
|
+
signal?.throwIfAborted();
|
|
9
|
+
const stream = source instanceof Response ? source.body : source;
|
|
10
|
+
if (stream === null)
|
|
11
|
+
return;
|
|
12
|
+
if (stream instanceof ReadableStream) {
|
|
13
|
+
const reader = stream.getReader();
|
|
14
|
+
const abort = () => {
|
|
15
|
+
if (signal)
|
|
16
|
+
reader.cancel(abortReason(signal)).catch(() => {
|
|
17
|
+
return;
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
21
|
+
try {
|
|
22
|
+
for (;; ) {
|
|
23
|
+
const pending = reader.read();
|
|
24
|
+
const chunk = await (signal ? untilAborted(pending, signal) : pending);
|
|
25
|
+
signal?.throwIfAborted();
|
|
26
|
+
if (chunk.done)
|
|
27
|
+
return;
|
|
28
|
+
yield chunk.value;
|
|
29
|
+
}
|
|
30
|
+
} finally {
|
|
31
|
+
signal?.removeEventListener("abort", abort);
|
|
32
|
+
if (signal?.aborted) {
|
|
33
|
+
reader.cancel(abortReason(signal)).catch(() => {
|
|
34
|
+
return;
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
await reader.cancel().catch(() => {
|
|
38
|
+
return;
|
|
39
|
+
});
|
|
40
|
+
reader.releaseLock();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
45
|
+
let finished = false;
|
|
46
|
+
try {
|
|
47
|
+
for (;; ) {
|
|
48
|
+
const pending = iterator.next();
|
|
49
|
+
const next = await (signal ? untilAborted(pending, signal) : pending);
|
|
50
|
+
if (next.done) {
|
|
51
|
+
finished = true;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
yield next.value;
|
|
55
|
+
}
|
|
56
|
+
} finally {
|
|
57
|
+
if (!finished && signal?.aborted)
|
|
58
|
+
iterator.return?.().catch(() => {
|
|
59
|
+
return;
|
|
60
|
+
});
|
|
61
|
+
else if (!finished)
|
|
62
|
+
await iterator.return?.().catch(() => {
|
|
63
|
+
return;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { chunksOf };
|
|
@@ -27,6 +27,14 @@ import {
|
|
|
27
27
|
bindContract,
|
|
28
28
|
mediaTypeEssence
|
|
29
29
|
} from "./index-pzw0fpek.js";
|
|
30
|
+
import {
|
|
31
|
+
readBoundedChunks,
|
|
32
|
+
concatChunks,
|
|
33
|
+
readBoundedBody2
|
|
34
|
+
} from "./index-qndsr7f0.js";
|
|
35
|
+
import {
|
|
36
|
+
abortReason
|
|
37
|
+
} from "./index-a5qr2rj6.js";
|
|
30
38
|
import {
|
|
31
39
|
assertPositiveSafeInteger
|
|
32
40
|
} from "./index-0s4xebbg.js";
|
|
@@ -418,27 +426,10 @@ async function collectFile(part, stream) {
|
|
|
418
426
|
});
|
|
419
427
|
}
|
|
420
428
|
async function readText(stream, maxBytes) {
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const result = await reader.read();
|
|
426
|
-
if (result.done)
|
|
427
|
-
break;
|
|
428
|
-
total += result.value.length;
|
|
429
|
-
if (total > maxBytes) {
|
|
430
|
-
await reader.cancel();
|
|
431
|
-
badRequest2(`Multipart text field exceeds ${maxBytes} bytes`);
|
|
432
|
-
}
|
|
433
|
-
chunks.push(result.value);
|
|
434
|
-
}
|
|
435
|
-
const value = new Uint8Array(total);
|
|
436
|
-
let offset = 0;
|
|
437
|
-
for (const chunk of chunks) {
|
|
438
|
-
value.set(chunk, offset);
|
|
439
|
-
offset += chunk.length;
|
|
440
|
-
}
|
|
441
|
-
return decoder.decode(value);
|
|
429
|
+
const result = await readBoundedChunks(stream, { maxBytes });
|
|
430
|
+
if (result.kind === "over-limit")
|
|
431
|
+
badRequest2(`Multipart text field exceeds ${maxBytes} bytes`);
|
|
432
|
+
return decoder.decode(concatChunks(result.chunks, result.total));
|
|
442
433
|
}
|
|
443
434
|
function required(policy) {
|
|
444
435
|
return policy.required !== false;
|
|
@@ -896,63 +887,16 @@ function mergeContextContribution(ctx, value, scope) {
|
|
|
896
887
|
}
|
|
897
888
|
|
|
898
889
|
// src/server/request-body.ts
|
|
899
|
-
function requestAbortReason(req) {
|
|
900
|
-
return req.signal.reason ?? new DOMException("The connection was closed", "AbortError");
|
|
901
|
-
}
|
|
902
|
-
function throwIfRequestAborted(req) {
|
|
903
|
-
if (req.signal.aborted)
|
|
904
|
-
throw requestAbortReason(req);
|
|
905
|
-
}
|
|
906
890
|
async function readRequestText(req, maxBytes) {
|
|
907
|
-
|
|
891
|
+
if (req.signal.aborted)
|
|
892
|
+
throw abortReason(req.signal);
|
|
908
893
|
if (maxBytes === undefined)
|
|
909
894
|
return req.text();
|
|
910
895
|
assertPositiveSafeInteger("maxJsonBodyBytes", maxBytes, Error);
|
|
911
|
-
const
|
|
912
|
-
if (
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
let total = 0;
|
|
916
|
-
let rejectAbortedRead = (_reason) => {
|
|
917
|
-
return;
|
|
918
|
-
};
|
|
919
|
-
const abortedRead = new Promise((_resolve, reject) => {
|
|
920
|
-
rejectAbortedRead = reject;
|
|
921
|
-
});
|
|
922
|
-
const onAbort = () => {
|
|
923
|
-
const reason = requestAbortReason(req);
|
|
924
|
-
rejectAbortedRead(reason);
|
|
925
|
-
reader.cancel(reason).catch(() => {
|
|
926
|
-
return;
|
|
927
|
-
});
|
|
928
|
-
};
|
|
929
|
-
req.signal.addEventListener("abort", onAbort, { once: true });
|
|
930
|
-
if (req.signal.aborted)
|
|
931
|
-
onAbort();
|
|
932
|
-
try {
|
|
933
|
-
while (true) {
|
|
934
|
-
const { done, value } = await Promise.race([reader.read(), abortedRead]);
|
|
935
|
-
if (done)
|
|
936
|
-
break;
|
|
937
|
-
total += value.byteLength;
|
|
938
|
-
if (total > maxBytes) {
|
|
939
|
-
await reader.cancel();
|
|
940
|
-
badRequest2(`JSON body exceeds the ${maxBytes}-byte limit`);
|
|
941
|
-
}
|
|
942
|
-
chunks.push(value);
|
|
943
|
-
}
|
|
944
|
-
} finally {
|
|
945
|
-
req.signal.removeEventListener("abort", onAbort);
|
|
946
|
-
if (!req.signal.aborted)
|
|
947
|
-
reader.releaseLock();
|
|
948
|
-
}
|
|
949
|
-
const bytes = new Uint8Array(total);
|
|
950
|
-
let offset = 0;
|
|
951
|
-
for (const chunk of chunks) {
|
|
952
|
-
bytes.set(chunk, offset);
|
|
953
|
-
offset += chunk.byteLength;
|
|
954
|
-
}
|
|
955
|
-
return new TextDecoder().decode(bytes);
|
|
896
|
+
const result = await readBoundedChunks(req.body, { maxBytes, signal: req.signal });
|
|
897
|
+
if (result.kind === "over-limit")
|
|
898
|
+
badRequest2(`JSON body exceeds the ${maxBytes}-byte limit`);
|
|
899
|
+
return new TextDecoder().decode(concatChunks(result.chunks, result.total));
|
|
956
900
|
}
|
|
957
901
|
|
|
958
902
|
// src/server/context.ts
|
|
@@ -1979,13 +1923,25 @@ function logOutgoing(entry) {
|
|
|
1979
1923
|
|
|
1980
1924
|
// src/server/request-completion.ts
|
|
1981
1925
|
var BODY_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
1926
|
+
var DEFAULT_PAYLOAD_LOG_BYTES = 1024 * 1024;
|
|
1927
|
+
async function readPayload(clone, maxBytes) {
|
|
1928
|
+
try {
|
|
1929
|
+
const result = await readBoundedChunks(clone.body, { maxBytes, signal: clone.signal });
|
|
1930
|
+
if (result.kind === "over-limit")
|
|
1931
|
+
return { value: undefined, truncated: true };
|
|
1932
|
+
return {
|
|
1933
|
+
value: JSON.parse(new TextDecoder().decode(concatChunks(result.chunks, result.total))),
|
|
1934
|
+
truncated: false
|
|
1935
|
+
};
|
|
1936
|
+
} catch {
|
|
1937
|
+
return { value: undefined, truncated: false };
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1982
1940
|
function openRequestCompletion(state, window) {
|
|
1983
1941
|
const { logConfig, customLogger, useDefaultLog, observability } = state;
|
|
1984
1942
|
const { req, url, traceId, startedAt, ipAddress } = window;
|
|
1985
1943
|
const shouldLogRequest = logConfig !== null && shouldLog(url.pathname, req.method) && !shouldSkipLog(logConfig, req, url);
|
|
1986
|
-
const
|
|
1987
|
-
return;
|
|
1988
|
-
}) : undefined;
|
|
1944
|
+
const payloadRead = observability?.includePayload && BODY_METHODS.has(req.method) ? readPayload(req.clone(), observability.payloadMaxBytes ?? state.config.maxJsonBodyBytes ?? DEFAULT_PAYLOAD_LOG_BYTES) : undefined;
|
|
1989
1945
|
const logFormat = resolveLogFormat(logConfig?.format);
|
|
1990
1946
|
let reqLog;
|
|
1991
1947
|
if (shouldLogRequest && useDefaultLog) {
|
|
@@ -2026,7 +1982,10 @@ function openRequestCompletion(state, window) {
|
|
|
2026
1982
|
context,
|
|
2027
1983
|
statusCode: status,
|
|
2028
1984
|
durationMs,
|
|
2029
|
-
|
|
1985
|
+
...payloadRead && {
|
|
1986
|
+
payload: payloadRead.then((read) => read.value),
|
|
1987
|
+
payloadTruncated: payloadRead.then((read) => read.truncated)
|
|
1988
|
+
},
|
|
2030
1989
|
...outcome !== undefined && { outcome }
|
|
2031
1990
|
});
|
|
2032
1991
|
} catch {}
|
|
@@ -2367,6 +2326,23 @@ function bindProcessSignals2(handle, options = {}) {
|
|
|
2367
2326
|
return { promise, close };
|
|
2368
2327
|
}
|
|
2369
2328
|
|
|
2329
|
+
// src/server/request-ingress.ts
|
|
2330
|
+
function defineRequestIngress2(name) {
|
|
2331
|
+
const values = new WeakMap;
|
|
2332
|
+
return {
|
|
2333
|
+
name,
|
|
2334
|
+
attach(request, value) {
|
|
2335
|
+
if (values.has(request)) {
|
|
2336
|
+
throw new TypeError(`[stitchkit] request ingress ${JSON.stringify(name)} is already attached to this request`);
|
|
2337
|
+
}
|
|
2338
|
+
values.set(request, value);
|
|
2339
|
+
},
|
|
2340
|
+
from(source) {
|
|
2341
|
+
return source.req && values.get(source.req);
|
|
2342
|
+
}
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2370
2346
|
// src/server/websocket.ts
|
|
2371
2347
|
function webSocketLane2(lane) {
|
|
2372
2348
|
const { match, handlers } = lane;
|
|
@@ -2630,39 +2606,17 @@ import {
|
|
|
2630
2606
|
|
|
2631
2607
|
// src/server/unix-client-body.ts
|
|
2632
2608
|
async function readBoundedRequestBody(request, maxBytes) {
|
|
2633
|
-
|
|
2634
|
-
if (!body)
|
|
2609
|
+
if (!request.body)
|
|
2635
2610
|
return;
|
|
2611
|
+
const tooLarge = () => new UnixClientTransportError2("UNIX_REQUEST_TOO_LARGE", `Unix request body exceeds the ${maxBytes} byte limit`, "not-dispatched");
|
|
2636
2612
|
const declared = request.headers.get("content-length");
|
|
2637
|
-
if (declared !== null && Number(declared) > maxBytes)
|
|
2638
|
-
throw
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
for (;; ) {
|
|
2645
|
-
if (request.signal.aborted)
|
|
2646
|
-
throw request.signal.reason;
|
|
2647
|
-
const chunk = await reader.read();
|
|
2648
|
-
if (chunk.done)
|
|
2649
|
-
break;
|
|
2650
|
-
total += chunk.value.byteLength;
|
|
2651
|
-
if (total > maxBytes) {
|
|
2652
|
-
throw new UnixClientTransportError2("UNIX_REQUEST_TOO_LARGE", `Unix request body exceeds the ${maxBytes} byte limit`, "not-dispatched");
|
|
2653
|
-
}
|
|
2654
|
-
chunks.push(chunk.value);
|
|
2655
|
-
}
|
|
2656
|
-
} finally {
|
|
2657
|
-
reader.releaseLock();
|
|
2658
|
-
}
|
|
2659
|
-
const result = new Uint8Array(total);
|
|
2660
|
-
let offset = 0;
|
|
2661
|
-
for (const chunk of chunks) {
|
|
2662
|
-
result.set(chunk, offset);
|
|
2663
|
-
offset += chunk.byteLength;
|
|
2664
|
-
}
|
|
2665
|
-
return result;
|
|
2613
|
+
if (declared !== null && Number(declared) > maxBytes)
|
|
2614
|
+
throw tooLarge();
|
|
2615
|
+
return readBoundedBody2(request.body, {
|
|
2616
|
+
maxBytes,
|
|
2617
|
+
signal: request.signal,
|
|
2618
|
+
limitError: tooLarge
|
|
2619
|
+
});
|
|
2666
2620
|
}
|
|
2667
2621
|
function incomingResponseBody(response, maxBytes, onDone) {
|
|
2668
2622
|
let finished = false;
|
|
@@ -3474,4 +3428,4 @@ function createUnixClientTransport2(config) {
|
|
|
3474
3428
|
};
|
|
3475
3429
|
}
|
|
3476
3430
|
|
|
3477
|
-
export { contextContributionDelta, prepareContextContribution, mergeContextContribution, parseMultipart2, DEFAULT_STREAM_HEARTBEAT_MS2, streamingRoute2, ndjsonRoute2, sseRoute2, createHandler2, createScopedImplementRegistry2, implementRegistry2, createImplementRegistry2, bindProcessSignals2, webSocketLane2, composeWebSocketHandlers2, createSocketIOServer2, socketIoLane2, UnixClientTransportError2, createUnixClientTransport2 };
|
|
3431
|
+
export { contextContributionDelta, prepareContextContribution, mergeContextContribution, parseMultipart2, DEFAULT_STREAM_HEARTBEAT_MS2, streamingRoute2, ndjsonRoute2, sseRoute2, createHandler2, createScopedImplementRegistry2, implementRegistry2, createImplementRegistry2, bindProcessSignals2, defineRequestIngress2, webSocketLane2, composeWebSocketHandlers2, createSocketIOServer2, socketIoLane2, UnixClientTransportError2, createUnixClientTransport2 };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chunksOf
|
|
3
|
+
} from "./index-cjbn98zt.js";
|
|
4
|
+
import {
|
|
5
|
+
abortReason
|
|
6
|
+
} from "./index-a5qr2rj6.js";
|
|
7
|
+
import {
|
|
8
|
+
assertPositiveSafeInteger
|
|
9
|
+
} from "./index-0s4xebbg.js";
|
|
10
|
+
|
|
11
|
+
// src/internal/bounded-body.ts
|
|
12
|
+
async function readBoundedChunks(source, options) {
|
|
13
|
+
options.signal?.throwIfAborted();
|
|
14
|
+
const chunks = [];
|
|
15
|
+
let total = 0;
|
|
16
|
+
if (source === null)
|
|
17
|
+
return { kind: "complete", chunks, total };
|
|
18
|
+
for await (const chunk of chunksOf(source, options.signal)) {
|
|
19
|
+
if (!(chunk instanceof Uint8Array)) {
|
|
20
|
+
throw new TypeError("A bounded body source must yield bytes (Uint8Array chunks)");
|
|
21
|
+
}
|
|
22
|
+
total += chunk.byteLength;
|
|
23
|
+
options.onChunk?.(chunk);
|
|
24
|
+
if (total > options.maxBytes)
|
|
25
|
+
return { kind: "over-limit", total };
|
|
26
|
+
chunks.push(chunk);
|
|
27
|
+
}
|
|
28
|
+
return { kind: "complete", chunks, total };
|
|
29
|
+
}
|
|
30
|
+
function concatChunks(chunks, total) {
|
|
31
|
+
const bytes = new Uint8Array(total);
|
|
32
|
+
let offset = 0;
|
|
33
|
+
for (const chunk of chunks) {
|
|
34
|
+
bytes.set(chunk, offset);
|
|
35
|
+
offset += chunk.byteLength;
|
|
36
|
+
}
|
|
37
|
+
return bytes;
|
|
38
|
+
}
|
|
39
|
+
async function readBoundedBody2(source, options) {
|
|
40
|
+
assertPositiveSafeInteger("maxBytes", options.maxBytes);
|
|
41
|
+
let result;
|
|
42
|
+
try {
|
|
43
|
+
result = await readBoundedChunks(source, options);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const { signal, abortError } = options;
|
|
46
|
+
if (signal?.aborted && abortError)
|
|
47
|
+
throw abortError(abortReason(signal));
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
if (result.kind === "over-limit")
|
|
51
|
+
throw options.limitError(result.total);
|
|
52
|
+
return concatChunks(result.chunks, result.total);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { readBoundedChunks, concatChunks, readBoundedBody2 };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
raceAbort
|
|
3
3
|
} from "./index-b0kddvzw.js";
|
|
4
|
+
import {
|
|
5
|
+
readBoundedChunks
|
|
6
|
+
} from "./index-qndsr7f0.js";
|
|
4
7
|
|
|
5
8
|
// src/internal/secure-fetch.ts
|
|
6
9
|
import { lookup } from "node:dns/promises";
|
|
@@ -259,27 +262,8 @@ async function fetchPinnedDocument(start, options) {
|
|
|
259
262
|
throw new Error("too many redirects");
|
|
260
263
|
}
|
|
261
264
|
async function readCapped(res, max) {
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
return Buffer.alloc(0);
|
|
265
|
-
const chunks = [];
|
|
266
|
-
let total = 0;
|
|
267
|
-
try {
|
|
268
|
-
while (true) {
|
|
269
|
-
const { done, value } = await reader.read();
|
|
270
|
-
if (done)
|
|
271
|
-
break;
|
|
272
|
-
total += value.length;
|
|
273
|
-
if (total > max) {
|
|
274
|
-
await reader.cancel();
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
chunks.push(value);
|
|
278
|
-
}
|
|
279
|
-
} finally {
|
|
280
|
-
reader.releaseLock();
|
|
281
|
-
}
|
|
282
|
-
return Buffer.concat(chunks);
|
|
265
|
+
const result = await readBoundedChunks(res.body, { maxBytes: max });
|
|
266
|
+
return result.kind === "over-limit" ? null : Buffer.concat(result.chunks);
|
|
283
267
|
}
|
|
284
268
|
|
|
285
269
|
export { fetchGuarded, PrivateAddressRefusal, fetchPinnedDocument, readCapped };
|