



























@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
22import fs from "node:fs/promises";
33import os from "node:os";
44import path from "node:path";
5+import { readResponseWithLimit } from "../media/read-response-with-limit.js";
56import {
67normalizeLowercaseStringOrEmpty,
78normalizeOptionalString,
@@ -636,85 +637,17 @@ async function fetchJson<T>(params: ClawHubRequestParams): Promise<T> {
636637return (await response.json()) as T;
637638}
638639639-function buildClawHubBodyTimeoutError(resourceLabel: string, timeoutMs: number): Error {
640-return new Error(`ClawHub ${resourceLabel} body stalled after ${timeoutMs}ms`);
641-}
642-643-async function readClawHubBodyChunkWithTimeout(params: {
644-reader: ReadableStreamDefaultReader<Uint8Array>;
645-timeoutMs: number;
646-resourceLabel: string;
647-}): Promise<ReadableStreamReadResult<Uint8Array>> {
648-return await new Promise((resolve, reject) => {
649-let timeout: ReturnType<typeof setTimeout> | undefined;
650-let settled = false;
651-const settle = (fn: () => void) => {
652-if (settled) {
653-return;
654-}
655-settled = true;
656-if (timeout !== undefined) {
657-clearTimeout(timeout);
658-}
659-fn();
660-};
661-662-timeout = setTimeout(() => {
663-const error = buildClawHubBodyTimeoutError(params.resourceLabel, params.timeoutMs);
664-void params.reader.cancel(error).catch(() => undefined);
665-settle(() => reject(error));
666-}, params.timeoutMs);
667-668-void params.reader.read().then(
669-(result) => settle(() => resolve(result)),
670-(error: unknown) => settle(() => reject(error)),
671-);
672-});
673-}
674-675640async function readClawHubResponseBytes(params: {
676641response: Response;
677642timeoutMs?: number;
678643resourceLabel: string;
679644}): Promise<Uint8Array> {
680645const timeoutMs = params.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
681-const body = params.response.body;
682-if (!body || typeof body.getReader !== "function") {
683-return new Uint8Array(await params.response.arrayBuffer());
684-}
685-686-const reader = body.getReader();
687-const chunks: Uint8Array[] = [];
688-let total = 0;
689-try {
690-while (true) {
691-const { done, value } = await readClawHubBodyChunkWithTimeout({
692- reader,
693- timeoutMs,
694-resourceLabel: params.resourceLabel,
695-});
696-if (done) {
697-break;
698-}
699-if (!value?.length) {
700-continue;
701-}
702-chunks.push(value);
703-total += value.length;
704-}
705-} finally {
706-try {
707-reader.releaseLock();
708-} catch {}
709-}
710-711-const bytes = new Uint8Array(total);
712-let offset = 0;
713-for (const chunk of chunks) {
714-bytes.set(chunk, offset);
715-offset += chunk.length;
716-}
717-return bytes;
646+return await readResponseWithLimit(params.response, Number.MAX_SAFE_INTEGER, {
647+chunkTimeoutMs: timeoutMs,
648+onIdleTimeout: ({ chunkTimeoutMs }) =>
649+new Error(`ClawHub ${params.resourceLabel} body stalled after ${chunkTimeoutMs}ms`),
650+});
718651}
719652720653export function resolveClawHubBaseUrl(baseUrl?: string): string {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。