feat: integrate Pi backend and Pi Web

This commit is contained in:
luckyyzh
2026-07-30 19:37:53 +08:00
commit 7392ab9dd7
1390 changed files with 337197 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
import { EventEmitter } from "node:events";
import * as undici from "undici";
export const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;
type DispatcherGlobal = typeof globalThis & {
__piWebHttpDispatcherConfigured?: boolean;
};
const dispatcherGlobal = globalThis as DispatcherGlobal;
const originalGlobalFetch = globalThis.fetch;
const ignoreUndiciDispatcherError = (): void => {};
function parseHttpIdleTimeoutMs(value: unknown): number | undefined {
if (typeof value === "string") {
const trimmed = value.trim();
if (trimmed.toLowerCase() === "disabled") return 0;
if (trimmed.length === 0) return undefined;
return parseHttpIdleTimeoutMs(Number(trimmed));
}
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
return undefined;
}
return Math.floor(value);
}
// Undici can emit an internal Client error while terminating a response body.
// The body stream still rejects; this prevents the EventEmitter error from
// terminating the Next.js process first.
function withUndiciErrorListener<T extends undici.Dispatcher>(dispatcher: T): T {
if (dispatcher instanceof EventEmitter) {
EventEmitter.prototype.on.call(dispatcher, "error", ignoreUndiciDispatcherError);
}
return dispatcher;
}
function createUndiciClient(origin: string | URL, options: object): undici.Dispatcher {
return withUndiciErrorListener(
new undici.Client(origin, options as undici.Client.Options),
);
}
function createUndiciOriginDispatcher(origin: string | URL, options: object): undici.Dispatcher {
const dispatcherOptions = options as undici.Pool.Options;
if (dispatcherOptions.connections === 1) {
return createUndiciClient(origin, dispatcherOptions);
}
return withUndiciErrorListener(
new undici.Pool(origin, {
...dispatcherOptions,
factory: createUndiciClient,
}),
);
}
export function configureHttpDispatcher(
timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS,
): void {
if (dispatcherGlobal.__piWebHttpDispatcherConfigured) return;
const normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);
if (normalizedTimeoutMs === undefined) {
throw new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);
}
const dispatcher = withUndiciErrorListener(
new undici.EnvHttpProxyAgent({
allowH2: false,
bodyTimeout: normalizedTimeoutMs,
headersTimeout: normalizedTimeoutMs,
clientFactory: createUndiciClient,
factory: createUndiciOriginDispatcher,
}),
);
undici.setGlobalDispatcher(dispatcher);
// Keep fetch and the dispatcher on the same undici implementation. Preserve
// an intentional fetch override installed after this module was loaded.
if (globalThis.fetch === originalGlobalFetch) {
undici.install?.();
}
dispatcherGlobal.__piWebHttpDispatcherConfigured = true;
}