@pasko70/pibo 3.4.4 → 3.5.1
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/routed-session.js +111 -25
- package/dist/agent-runtimes/codex-native/turn.js +27 -3
- package/dist/agent-runtimes/pi/adapter.js +1 -0
- package/dist/agent-runtimes/pi/routed-session.js +2 -2
- package/dist/apps/chat/bounded-event-stream.js +98 -0
- package/dist/apps/chat/chat-settings-routes.js +3 -3
- package/dist/apps/chat/data/chat-data-mappers.js +40 -14
- package/dist/apps/chat/data/event-command-service.js +30 -21
- package/dist/apps/chat/data/history-query-service.js +34 -25
- package/dist/apps/chat/data/read-state-service.js +37 -3
- package/dist/apps/chat/data/session-query-service.js +19 -11
- package/dist/apps/chat/data/timeline-query-service.js +19 -7
- package/dist/apps/chat/message-command-dispatcher.js +149 -0
- package/dist/apps/chat/output-compactor.js +9 -0
- package/dist/apps/chat/output-event-policy.js +9 -1
- package/dist/apps/chat/stream.js +21 -3
- package/dist/apps/chat/telemetry-retention-service.js +113 -8
- package/dist/apps/chat/trace-response-cache.js +46 -0
- package/dist/apps/chat/trace-v2.js +12 -6
- package/dist/apps/chat/trace.js +1 -1
- package/dist/apps/chat/web-app.js +645 -297
- package/dist/apps/chat-ui/assets/{dist-V06sfuZa.js → dist-B-auLrzD.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CH3SvpYV.js → dist-BA_dsINH.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-23lt_7qm.js → dist-eJZar_0-.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Bf2KScPo.js → dist-wNNR2Bci.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DsgL8w-W.js → dist-zcmEsIEp.js} +1 -1
- package/dist/apps/chat-ui/assets/index-DEkbN5Vo.js +229 -0
- package/dist/apps/chat-ui/assets/index-DZK6Tzil.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-DZgW1fCB.js +44 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/cli-session/localSessionSource.js +3 -2
- package/dist/compute/pool/seeds.js +25 -3
- package/dist/core/events.js +4 -0
- package/dist/core/output-render-sequence.js +65 -6
- package/dist/core/provider-capacity.js +33 -0
- package/dist/core/provider-telemetry.js +21 -6
- package/dist/core/runtime-capacity.js +174 -0
- package/dist/core/runtime-telemetry.js +40 -12
- package/dist/core/session-router.js +178 -13
- package/dist/data/async-chat-reads.js +38 -0
- package/dist/data/async-chat-storage.js +96 -0
- package/dist/data/async-telemetry-maintenance.js +9 -0
- package/dist/data/bounded-worker-client.js +256 -0
- package/dist/data/chat-read-projections.js +159 -0
- package/dist/data/chat-read-worker.js +73 -0
- package/dist/data/chat-storage-worker.js +167 -0
- package/dist/data/ingest-service.js +152 -18
- package/dist/data/message-command-store.js +290 -0
- package/dist/data/payload-store.js +92 -11
- package/dist/data/pibo-store.js +10 -8
- package/dist/data/schema.js +39 -4
- package/dist/data/session-store.js +3 -1
- package/dist/data/storage-backup.js +278 -0
- package/dist/data/storage-maintenance.js +344 -0
- package/dist/data/storage-verification-worker.js +25 -0
- package/dist/data/telemetry-capture.js +188 -0
- package/dist/data/telemetry-command.js +3 -0
- package/dist/data/telemetry-maintenance-worker.js +40 -0
- package/dist/data/telemetry-maintenance.js +110 -0
- package/dist/data/telemetry-retention.js +16 -7
- package/dist/data/telemetry-worker.js +111 -0
- package/dist/data/telemetry-writer.js +150 -83
- package/dist/data/telemetry.js +5 -0
- package/dist/debug/index.js +207 -1
- package/dist/debug/message-queue.js +108 -0
- package/dist/debug/output-collision-repair.js +140 -0
- package/dist/debug/output-integrity.js +38 -2
- package/dist/debug/output-repair.js +1 -0
- package/dist/debug/storage-backup.js +41 -0
- package/dist/debug/storage-maintenance.js +78 -0
- package/dist/debug/telemetry-capture.js +66 -0
- package/dist/gateway/cli.js +71 -7
- package/dist/gateway/server.js +3 -0
- package/dist/providers/openai-gpt56.js +11 -6
- package/dist/reliability/store.js +119 -23
- package/dist/session-ui/terminalRows.js +54 -13
- package/dist/sessions/pibo-data-store.js +19 -14
- package/dist/shared/debug-features.js +4 -0
- package/dist/shared/model-inference-metrics.js +23 -0
- package/dist/shared/trace-event-projection.js +69 -2
- package/dist/shared/trace-history.js +9 -0
- package/dist/shared/trace-live-reducer.js +1 -0
- package/dist/shared/trace-patch-nodes.js +19 -0
- package/dist/web/channel.js +117 -12
- package/dist/web/http.js +135 -41
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/apps/chat-ui/assets/index-BOceJ0jM.css +0 -1
- package/dist/apps/chat-ui/assets/index-BOemYq-V.js +0 -228
- package/dist/apps/chat-vscode-web/assets/index-CMwTB8o8.js +0 -43
package/dist/web/channel.js
CHANGED
|
@@ -191,15 +191,31 @@ function createGatewayRuntimeStatuses(channelContext) {
|
|
|
191
191
|
}
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
|
-
function createGatewayStatusResponse(channelContext, options, generation) {
|
|
194
|
+
async function createGatewayStatusResponse(channelContext, options, generation) {
|
|
195
195
|
const mode = gatewayMode(options);
|
|
196
|
+
const appStatuses = {};
|
|
197
|
+
for (const app of channelContext.getWebApps()) {
|
|
198
|
+
if (!app.gatewayStatus)
|
|
199
|
+
continue;
|
|
200
|
+
try {
|
|
201
|
+
Object.assign(appStatuses, await app.gatewayStatus());
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
appStatuses[`${app.name}Status`] = { status: "ambiguous", error: error instanceof Error ? error.message : "Status unavailable" };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const durable = appStatuses.durableMessageQueue;
|
|
196
208
|
return responseJson({
|
|
197
|
-
status: "ok",
|
|
209
|
+
status: durable?.status === "degraded" || durable?.status === "ambiguous" ? "degraded" : "ok",
|
|
198
210
|
mode,
|
|
199
211
|
generation,
|
|
200
|
-
health: { status: "ok", mode },
|
|
212
|
+
health: { status: durable?.status === "degraded" || durable?.status === "ambiguous" ? "degraded" : "ok", mode },
|
|
213
|
+
runtimeQueue: { layer: "runtime-session", statuses: createGatewayRuntimeStatuses(channelContext) },
|
|
201
214
|
runtimeStatuses: createGatewayRuntimeStatuses(channelContext),
|
|
215
|
+
...(channelContext.getRuntimeCapacityStatus ? { runtimeCapacity: channelContext.getRuntimeCapacityStatus() } : {}),
|
|
216
|
+
...(channelContext.getRunJobReliabilityStatus ? { reliability: channelContext.getRunJobReliabilityStatus() } : {}),
|
|
202
217
|
activeRuns: collectActiveRuns(channelContext),
|
|
218
|
+
...appStatuses,
|
|
203
219
|
});
|
|
204
220
|
}
|
|
205
221
|
function createCanonicalRedirect(request, canonicalBaseURL) {
|
|
@@ -217,6 +233,68 @@ function createCanonicalRedirect(request, canonicalBaseURL) {
|
|
|
217
233
|
function isEventStreamResponse(response) {
|
|
218
234
|
return response.headers.get("content-type")?.toLowerCase().startsWith("text/event-stream") === true;
|
|
219
235
|
}
|
|
236
|
+
function responseCanStart(response) {
|
|
237
|
+
return !response.destroyed && !response.writableEnded && !response.writableFinished && !response.headersSent;
|
|
238
|
+
}
|
|
239
|
+
function responseState(response) {
|
|
240
|
+
return [
|
|
241
|
+
`destroyed=${response.destroyed}`,
|
|
242
|
+
`writableEnded=${response.writableEnded}`,
|
|
243
|
+
`writableFinished=${response.writableFinished}`,
|
|
244
|
+
`headersSent=${response.headersSent}`,
|
|
245
|
+
].join(",");
|
|
246
|
+
}
|
|
247
|
+
function requestPath(request) {
|
|
248
|
+
return (request.url ?? "/").split(/[?#]/, 1)[0].slice(0, 256).replace(/[\r\n]/g, "_");
|
|
249
|
+
}
|
|
250
|
+
function errorIdentity(error) {
|
|
251
|
+
try {
|
|
252
|
+
if (!(error instanceof Error))
|
|
253
|
+
return typeof error;
|
|
254
|
+
const rawName = typeof error.name === "string" ? error.name : "Error";
|
|
255
|
+
const name = rawName.replace(/[^A-Za-z0-9_.-]/g, "").slice(0, 64) || "Error";
|
|
256
|
+
const code = "code" in error && typeof error.code === "string" ? error.code.replace(/[^A-Za-z0-9_-]/g, "").slice(0, 64) : undefined;
|
|
257
|
+
return code ? `${name}:${code}` : name;
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return "Error";
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function logHttpBoundaryFailure(phase, request, error, response) {
|
|
264
|
+
try {
|
|
265
|
+
const method = (request.method ?? "UNKNOWN").replace(/[^A-Z]/gi, "").slice(0, 16) || "UNKNOWN";
|
|
266
|
+
const state = response ? ` response={${responseState(response)}}` : "";
|
|
267
|
+
console.error(`[web-host] contained ${phase} failure method=${method} path=${requestPath(request)} error=${errorIdentity(error)}${state}`);
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// Diagnostics are best-effort and must never reopen the request rejection path.
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function terminateResponse(response, error) {
|
|
274
|
+
if (response.destroyed || response.writableEnded || response.writableFinished)
|
|
275
|
+
return;
|
|
276
|
+
try {
|
|
277
|
+
response.destroy(error instanceof Error ? error : undefined);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
// A broken response implementation must not escape the request boundary.
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function endUpgradeSocket(socket, statusLine) {
|
|
284
|
+
if (socket.destroyed)
|
|
285
|
+
return;
|
|
286
|
+
try {
|
|
287
|
+
socket.end(statusLine);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
try {
|
|
291
|
+
socket.destroy();
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
// A broken socket implementation must not escape the upgrade boundary.
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
220
298
|
async function waitForServerClose(closePromise, timeoutMs) {
|
|
221
299
|
return await new Promise((resolve, reject) => {
|
|
222
300
|
let settled = false;
|
|
@@ -322,7 +400,7 @@ export function createWebHostChannel(options = {}) {
|
|
|
322
400
|
return;
|
|
323
401
|
}
|
|
324
402
|
if (url.pathname === "/gateway/status") {
|
|
325
|
-
await sendResponse(nodeResponse, createGatewayStatusResponse(requireContext(), options, generation));
|
|
403
|
+
await sendResponse(nodeResponse, await createGatewayStatusResponse(requireContext(), options, generation));
|
|
326
404
|
return;
|
|
327
405
|
}
|
|
328
406
|
if (url.pathname.startsWith("/api/auth/")) {
|
|
@@ -359,8 +437,18 @@ export function createWebHostChannel(options = {}) {
|
|
|
359
437
|
}
|
|
360
438
|
catch (error) {
|
|
361
439
|
const status = error instanceof PiboAuthError || error instanceof PiboWebHttpError ? error.statusCode : 500;
|
|
362
|
-
|
|
363
|
-
|
|
440
|
+
logHttpBoundaryFailure("request", nodeRequest, error, nodeResponse);
|
|
441
|
+
if (responseCanStart(nodeResponse)) {
|
|
442
|
+
try {
|
|
443
|
+
await sendResponse(nodeResponse, responseJson({ error: error instanceof Error ? error.message : String(error) }, { status }));
|
|
444
|
+
}
|
|
445
|
+
catch (responseError) {
|
|
446
|
+
logHttpBoundaryFailure("error-response", nodeRequest, responseError, nodeResponse);
|
|
447
|
+
terminateResponse(nodeResponse, responseError);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
terminateResponse(nodeResponse, error);
|
|
364
452
|
}
|
|
365
453
|
}
|
|
366
454
|
finally {
|
|
@@ -383,10 +471,9 @@ export function createWebHostChannel(options = {}) {
|
|
|
383
471
|
await app.handleUpgrade(nodeRequest, socket, head, createAppContext(ctx), requestURL);
|
|
384
472
|
}
|
|
385
473
|
catch (error) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
474
|
+
logHttpBoundaryFailure("upgrade", nodeRequest, error);
|
|
475
|
+
const unauthorized = error instanceof PiboAuthError || error instanceof PiboWebHttpError;
|
|
476
|
+
endUpgradeSocket(socket, `HTTP/1.1 ${unauthorized ? 401 : 502} ${unauthorized ? "Unauthorized" : "Bad Gateway"}\r\nConnection: close\r\n\r\n`);
|
|
390
477
|
}
|
|
391
478
|
};
|
|
392
479
|
return {
|
|
@@ -401,11 +488,29 @@ export function createWebHostChannel(options = {}) {
|
|
|
401
488
|
return;
|
|
402
489
|
shuttingDown = false;
|
|
403
490
|
context = channelContext;
|
|
491
|
+
for (const app of channelContext.getWebApps())
|
|
492
|
+
await app.initialize?.(createAppContext(channelContext));
|
|
404
493
|
server = createServer((request, response) => {
|
|
405
|
-
void handleRequest(request, response)
|
|
494
|
+
void handleRequest(request, response).catch((error) => {
|
|
495
|
+
try {
|
|
496
|
+
logHttpBoundaryFailure("request-terminal", request, error, response);
|
|
497
|
+
terminateResponse(response, error);
|
|
498
|
+
}
|
|
499
|
+
catch {
|
|
500
|
+
// The terminal request boundary itself is intentionally nonthrowing.
|
|
501
|
+
}
|
|
502
|
+
});
|
|
406
503
|
});
|
|
407
504
|
server.on("upgrade", (request, socket, head) => {
|
|
408
|
-
void handleUpgrade(request, socket, head)
|
|
505
|
+
void handleUpgrade(request, socket, head).catch((error) => {
|
|
506
|
+
try {
|
|
507
|
+
logHttpBoundaryFailure("upgrade-terminal", request, error);
|
|
508
|
+
endUpgradeSocket(socket);
|
|
509
|
+
}
|
|
510
|
+
catch {
|
|
511
|
+
// The terminal upgrade boundary itself is intentionally nonthrowing.
|
|
512
|
+
}
|
|
513
|
+
});
|
|
409
514
|
});
|
|
410
515
|
server.on("connection", (socket) => {
|
|
411
516
|
sockets.add(socket);
|
package/dist/web/http.js
CHANGED
|
@@ -78,61 +78,136 @@ export async function nodeRequestToWebRequest(request, baseURL, signal) {
|
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
export async function sendWebResponse(response, webResponse, options = {}) {
|
|
81
|
+
assertResponseCanStart(response);
|
|
81
82
|
const headers = responseHeaders(webResponse);
|
|
82
83
|
const compressEncoding = preferredResponseEncoding(response.req?.headers["accept-encoding"], webResponse);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
let reader = webResponse.body?.getReader();
|
|
85
|
+
let wroteHeaders = false;
|
|
86
|
+
let clientClosed = false;
|
|
87
|
+
const cancelReader = () => {
|
|
88
|
+
if (reader)
|
|
89
|
+
void reader.cancel().catch(() => undefined);
|
|
90
|
+
};
|
|
91
|
+
const close = () => {
|
|
92
|
+
clientClosed = true;
|
|
93
|
+
cancelReader();
|
|
94
|
+
};
|
|
95
|
+
const abort = () => {
|
|
96
|
+
cancelReader();
|
|
97
|
+
try {
|
|
98
|
+
if (!response.destroyed && !response.writableEnded)
|
|
99
|
+
response.end();
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
tryDestroyResponse(response);
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
response.socket?.end();
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
tryDestroyResponse(response);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
if (reader) {
|
|
112
|
+
response.once("close", close);
|
|
113
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
114
|
+
if (options.signal?.aborted)
|
|
115
|
+
abort();
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
if (compressEncoding && reader) {
|
|
119
|
+
const body = await readResponseBody(reader);
|
|
120
|
+
if (clientClosed || options.signal?.aborted || response.destroyed || response.writableEnded || response.writableFinished)
|
|
121
|
+
return;
|
|
122
|
+
assertResponseCanStart(response);
|
|
123
|
+
if (body.length >= MIN_COMPRESS_RESPONSE_BYTES && body.length <= MAX_SYNC_GZIP_RESPONSE_BYTES) {
|
|
124
|
+
const compressionStartedAt = performance.now();
|
|
125
|
+
const compressed = gzipSync(body, { level: 1 });
|
|
126
|
+
appendServerTimingHeader(headers, `response_compress;dur=${(performance.now() - compressionStartedAt).toFixed(1)}`);
|
|
127
|
+
headers["content-encoding"] = compressEncoding;
|
|
128
|
+
headers["content-length"] = String(compressed.length);
|
|
129
|
+
headers.vary = appendVary(headers.vary, "accept-encoding");
|
|
130
|
+
response.writeHead(webResponse.status, headers);
|
|
131
|
+
wroteHeaders = true;
|
|
132
|
+
response.end(compressed);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (body.length > MAX_SYNC_GZIP_RESPONSE_BYTES) {
|
|
136
|
+
headers["x-pibo-compression-skipped"] = "sync-gzip-size-limit";
|
|
137
|
+
}
|
|
92
138
|
response.writeHead(webResponse.status, headers);
|
|
93
|
-
|
|
139
|
+
wroteHeaders = true;
|
|
140
|
+
response.end(body);
|
|
94
141
|
return;
|
|
95
142
|
}
|
|
96
|
-
if (body.length > MAX_SYNC_GZIP_RESPONSE_BYTES) {
|
|
97
|
-
headers["x-pibo-compression-skipped"] = "sync-gzip-size-limit";
|
|
98
|
-
}
|
|
99
143
|
response.writeHead(webResponse.status, headers);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
response.writeHead(webResponse.status, headers);
|
|
104
|
-
if (!webResponse.body) {
|
|
105
|
-
response.end();
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
const reader = webResponse.body.getReader();
|
|
109
|
-
const cancel = () => {
|
|
110
|
-
void reader.cancel();
|
|
111
|
-
};
|
|
112
|
-
const abort = () => {
|
|
113
|
-
const socket = response.socket;
|
|
114
|
-
void reader.cancel();
|
|
115
|
-
if (!response.writableEnded)
|
|
144
|
+
wroteHeaders = true;
|
|
145
|
+
if (!reader) {
|
|
116
146
|
response.end();
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
response.once("close", cancel);
|
|
120
|
-
options.signal?.addEventListener("abort", abort, { once: true });
|
|
121
|
-
if (options.signal?.aborted)
|
|
122
|
-
abort();
|
|
123
|
-
try {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
124
149
|
while (true) {
|
|
125
150
|
const { done, value } = await reader.read();
|
|
126
151
|
if (done)
|
|
127
152
|
break;
|
|
128
|
-
|
|
153
|
+
const bytes = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
154
|
+
for (let offset = 0; offset < bytes.length; offset += 64 * 1024) {
|
|
155
|
+
if (response.destroyed || response.writableEnded || response.writableFinished) {
|
|
156
|
+
cancelReader();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (!response.write(bytes.subarray(offset, offset + 64 * 1024))) {
|
|
160
|
+
const drained = await waitForResponseDrain(response, (webResponse.headers.get("content-type") ?? "").startsWith("text/event-stream") ? 5000 : undefined, options.signal);
|
|
161
|
+
if (!drained) {
|
|
162
|
+
cancelReader();
|
|
163
|
+
tryDestroyResponse(response);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
129
168
|
}
|
|
130
|
-
if (!response.writableEnded)
|
|
169
|
+
if (!response.destroyed && !response.writableEnded && !response.writableFinished)
|
|
131
170
|
response.end();
|
|
132
171
|
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
cancelReader();
|
|
174
|
+
if (clientClosed || response.destroyed || response.writableEnded || response.writableFinished)
|
|
175
|
+
return;
|
|
176
|
+
if (wroteHeaders || response.headersSent)
|
|
177
|
+
tryDestroyResponse(response, error);
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
133
180
|
finally {
|
|
134
|
-
response.off("close",
|
|
181
|
+
response.off("close", close);
|
|
135
182
|
options.signal?.removeEventListener("abort", abort);
|
|
183
|
+
if (reader) {
|
|
184
|
+
try {
|
|
185
|
+
reader.releaseLock();
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Cancellation/read settlement owns the lock until the pending read resolves.
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function assertResponseCanStart(response) {
|
|
194
|
+
if (response.destroyed)
|
|
195
|
+
throw new Error("HTTP response is destroyed");
|
|
196
|
+
if (response.writableEnded)
|
|
197
|
+
throw new Error("HTTP response has ended");
|
|
198
|
+
if (response.writableFinished)
|
|
199
|
+
throw new Error("HTTP response has finished");
|
|
200
|
+
if (response.headersSent)
|
|
201
|
+
throw new Error("HTTP response headers were already sent");
|
|
202
|
+
}
|
|
203
|
+
function tryDestroyResponse(response, error) {
|
|
204
|
+
if (response.destroyed)
|
|
205
|
+
return;
|
|
206
|
+
try {
|
|
207
|
+
response.destroy(error instanceof Error ? error : undefined);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// The request boundary is best-effort once the response implementation itself fails.
|
|
136
211
|
}
|
|
137
212
|
}
|
|
138
213
|
function appendServerTimingHeader(headers, value) {
|
|
@@ -185,9 +260,8 @@ function responseCanBeCompressed(webResponse) {
|
|
|
185
260
|
const contentType = webResponse.headers.get("content-type") ?? "";
|
|
186
261
|
return /^application\/json\b/.test(contentType);
|
|
187
262
|
}
|
|
188
|
-
async function readResponseBody(
|
|
263
|
+
async function readResponseBody(reader) {
|
|
189
264
|
const chunks = [];
|
|
190
|
-
const reader = webResponse.body.getReader();
|
|
191
265
|
while (true) {
|
|
192
266
|
const { done, value } = await reader.read();
|
|
193
267
|
if (done)
|
|
@@ -206,3 +280,23 @@ function appendVary(existing, value) {
|
|
|
206
280
|
function appendServerTiming(existing, value) {
|
|
207
281
|
return existing ? `${existing}, ${value}` : value;
|
|
208
282
|
}
|
|
283
|
+
/** Honor socket pressure before pulling another application frame. */
|
|
284
|
+
function waitForResponseDrain(response, maximumAgeMs, signal) {
|
|
285
|
+
if (response.destroyed || response.writableEnded || signal?.aborted)
|
|
286
|
+
return Promise.resolve(false);
|
|
287
|
+
return new Promise(resolve => {
|
|
288
|
+
let timer;
|
|
289
|
+
const finish = (ready) => { if (timer)
|
|
290
|
+
clearTimeout(timer); response.off("drain", drain); response.off("close", close); response.off("error", close); signal?.removeEventListener("abort", close); resolve(ready); };
|
|
291
|
+
const drain = () => finish(true);
|
|
292
|
+
const close = () => finish(false);
|
|
293
|
+
response.once("drain", drain);
|
|
294
|
+
response.once("close", close);
|
|
295
|
+
response.once("error", close);
|
|
296
|
+
signal?.addEventListener("abort", close, { once: true });
|
|
297
|
+
if (maximumAgeMs !== undefined) {
|
|
298
|
+
timer = setTimeout(close, maximumAgeMs);
|
|
299
|
+
timer.unref();
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pasko70/pibo",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@pasko70/pibo",
|
|
9
|
-
"version": "3.
|
|
9
|
+
"version": "3.5.1",
|
|
10
10
|
"workspaces": [
|
|
11
11
|
"packages/workflows"
|
|
12
12
|
],
|