mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:50:43 +00:00
refactor: cache optional runtime imports
This commit is contained in:
@@ -67,19 +67,24 @@ type AzureIdentityModule = {
|
||||
|
||||
const AZURE_IDENTITY_MODULE = "@azure/identity";
|
||||
|
||||
let azureIdentityModulePromise: Promise<AzureIdentityModule> | null = null;
|
||||
|
||||
async function loadAzureIdentity(): Promise<AzureIdentityModule> {
|
||||
return (await import(AZURE_IDENTITY_MODULE)) as AzureIdentityModule;
|
||||
azureIdentityModulePromise ??= import(AZURE_IDENTITY_MODULE) as Promise<AzureIdentityModule>;
|
||||
return azureIdentityModulePromise;
|
||||
}
|
||||
|
||||
let msTeamsSdkPromise: Promise<MSTeamsTeamsSdk> | null = null;
|
||||
|
||||
export async function loadMSTeamsSdk(): Promise<MSTeamsTeamsSdk> {
|
||||
const [appsModule, apiModule] = await Promise.all([
|
||||
msTeamsSdkPromise ??= Promise.all([
|
||||
import("@microsoft/teams.apps"),
|
||||
import("@microsoft/teams.api"),
|
||||
]);
|
||||
return {
|
||||
]).then(([appsModule, apiModule]) => ({
|
||||
App: appsModule.App,
|
||||
Client: apiModule.Client,
|
||||
};
|
||||
}));
|
||||
return msTeamsSdkPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -653,6 +658,20 @@ const BOT_FRAMEWORK_ISSUERS: ReadonlyArray<{
|
||||
},
|
||||
];
|
||||
|
||||
type BotFrameworkJwtDeps = {
|
||||
jwt: typeof import("jsonwebtoken");
|
||||
JwksClient: typeof import("jwks-rsa").JwksClient;
|
||||
};
|
||||
|
||||
let botFrameworkJwtDepsPromise: Promise<BotFrameworkJwtDeps> | null = null;
|
||||
|
||||
async function loadBotFrameworkJwtDeps(): Promise<BotFrameworkJwtDeps> {
|
||||
botFrameworkJwtDepsPromise ??= Promise.all([import("jsonwebtoken"), import("jwks-rsa")]).then(
|
||||
([jwt, { JwksClient }]) => ({ jwt, JwksClient }),
|
||||
);
|
||||
return botFrameworkJwtDepsPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Bot Framework JWT validator using jsonwebtoken + jwks-rsa directly.
|
||||
*
|
||||
@@ -670,8 +689,7 @@ const BOT_FRAMEWORK_ISSUERS: ReadonlyArray<{
|
||||
export async function createBotFrameworkJwtValidator(creds: MSTeamsCredentials): Promise<{
|
||||
validate: (authHeader: string) => Promise<boolean>;
|
||||
}> {
|
||||
const jwt = await import("jsonwebtoken");
|
||||
const { JwksClient } = await import("jwks-rsa");
|
||||
const { jwt, JwksClient } = await loadBotFrameworkJwtDeps();
|
||||
|
||||
const allowedAudiences: [string, ...string[]] = [
|
||||
creds.appId,
|
||||
|
||||
@@ -728,10 +728,19 @@ function ffmpegToPCM(
|
||||
});
|
||||
}
|
||||
|
||||
type MpegDecoderConstructor = typeof import("mpg123-decoder").MPEGDecoder;
|
||||
|
||||
let mpegDecoderConstructorPromise: Promise<MpegDecoderConstructor> | null = null;
|
||||
|
||||
async function loadMpegDecoderConstructor(): Promise<MpegDecoderConstructor> {
|
||||
mpegDecoderConstructorPromise ??= import("mpg123-decoder").then(({ MPEGDecoder }) => MPEGDecoder);
|
||||
return mpegDecoderConstructorPromise;
|
||||
}
|
||||
|
||||
/** Decode MP3 into PCM through mpg123-decoder when ffmpeg is unavailable. */
|
||||
async function wasmDecodeMp3ToPCM(buf: Buffer, targetRate: number): Promise<Buffer | null> {
|
||||
try {
|
||||
const { MPEGDecoder } = await import("mpg123-decoder");
|
||||
const MPEGDecoder = await loadMpegDecoderConstructor();
|
||||
debugLog(`[audio-convert] WASM MP3 decode: size=${buf.length} bytes`);
|
||||
const decoder = new MPEGDecoder();
|
||||
await decoder.ready;
|
||||
|
||||
@@ -219,6 +219,18 @@ async function resolveEnvProxyAgent(
|
||||
});
|
||||
}
|
||||
|
||||
type UndiciProxyAgentsModule = Pick<typeof import("undici"), "EnvHttpProxyAgent" | "ProxyAgent">;
|
||||
|
||||
let undiciProxyAgentsModulePromise: Promise<UndiciProxyAgentsModule> | null = null;
|
||||
|
||||
async function loadUndiciProxyAgents(): Promise<UndiciProxyAgentsModule> {
|
||||
undiciProxyAgentsModulePromise ??= import("undici").then(({ EnvHttpProxyAgent, ProxyAgent }) => ({
|
||||
EnvHttpProxyAgent,
|
||||
ProxyAgent,
|
||||
}));
|
||||
return undiciProxyAgentsModulePromise;
|
||||
}
|
||||
|
||||
async function resolveEnvFetchDispatcher(
|
||||
logger: ReturnType<typeof getChildLogger>,
|
||||
agent?: unknown,
|
||||
@@ -229,7 +241,7 @@ async function resolveEnvFetchDispatcher(
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const { EnvHttpProxyAgent, ProxyAgent } = await import("undici");
|
||||
const { EnvHttpProxyAgent, ProxyAgent } = await loadUndiciProxyAgents();
|
||||
return proxyUrl
|
||||
? new ProxyAgent({ allowH2: false, uri: proxyUrl })
|
||||
: new EnvHttpProxyAgent({ allowH2: false });
|
||||
|
||||
@@ -136,6 +136,13 @@ function installCiaoConsoleNoiseFilter(): () => void {
|
||||
};
|
||||
}
|
||||
|
||||
let ciaoModulePromise: Promise<typeof import("@homebridge/ciao")> | null = null;
|
||||
|
||||
async function loadCiaoModule(): Promise<typeof import("@homebridge/ciao")> {
|
||||
ciaoModulePromise ??= import("@homebridge/ciao");
|
||||
return ciaoModulePromise;
|
||||
}
|
||||
|
||||
export async function startGatewayBonjourAdvertiser(
|
||||
opts: GatewayBonjourAdvertiseOpts,
|
||||
): Promise<GatewayBonjourAdvertiser> {
|
||||
@@ -143,7 +150,7 @@ export async function startGatewayBonjourAdvertiser(
|
||||
return { stop: async () => {} };
|
||||
}
|
||||
|
||||
const { getResponder, Protocol } = await import("@homebridge/ciao");
|
||||
const { getResponder, Protocol } = await loadCiaoModule();
|
||||
const restoreConsoleLog = installCiaoConsoleNoiseFilter();
|
||||
try {
|
||||
// mDNS service instance names are single DNS labels; dots in hostnames (like
|
||||
|
||||
@@ -4,6 +4,7 @@ import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
|
||||
import { runExec } from "../process/exec.js";
|
||||
|
||||
type Sharp = typeof import("sharp");
|
||||
type SharpFactory = (buffer: Buffer) => ReturnType<Sharp>;
|
||||
|
||||
export type ImageMetadata = {
|
||||
width: number;
|
||||
@@ -31,14 +32,18 @@ function prefersSips(): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
async function loadSharp(): Promise<(buffer: Buffer) => ReturnType<Sharp>> {
|
||||
const mod = (await import("sharp")) as unknown as { default?: Sharp };
|
||||
const sharp = mod.default ?? (mod as unknown as Sharp);
|
||||
return (buffer) =>
|
||||
sharp(buffer, {
|
||||
failOnError: false,
|
||||
limitInputPixels: MAX_IMAGE_INPUT_PIXELS,
|
||||
});
|
||||
let sharpFactoryPromise: Promise<SharpFactory> | null = null;
|
||||
|
||||
async function loadSharp(): Promise<SharpFactory> {
|
||||
sharpFactoryPromise ??= import("sharp").then((mod) => {
|
||||
const sharp = (mod as unknown as { default?: Sharp }).default ?? (mod as unknown as Sharp);
|
||||
return (buffer: Buffer) =>
|
||||
sharp(buffer, {
|
||||
failOnError: false,
|
||||
limitInputPixels: MAX_IMAGE_INPUT_PIXELS,
|
||||
});
|
||||
});
|
||||
return sharpFactoryPromise;
|
||||
}
|
||||
|
||||
function isPositiveImageDimension(value: number): boolean {
|
||||
|
||||
@@ -2,12 +2,19 @@ import type { DatabaseSync } from "node:sqlite";
|
||||
import { formatErrorMessage } from "../../infra/errors.js";
|
||||
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
||||
|
||||
let sqliteVecModulePromise: Promise<typeof import("sqlite-vec")> | null = null;
|
||||
|
||||
async function loadSqliteVecModule(): Promise<typeof import("sqlite-vec")> {
|
||||
sqliteVecModulePromise ??= import("sqlite-vec");
|
||||
return sqliteVecModulePromise;
|
||||
}
|
||||
|
||||
export async function loadSqliteVecExtension(params: {
|
||||
db: DatabaseSync;
|
||||
extensionPath?: string;
|
||||
}): Promise<{ ok: boolean; extensionPath?: string; error?: string }> {
|
||||
try {
|
||||
const sqliteVec = await import("sqlite-vec");
|
||||
const sqliteVec = await loadSqliteVecModule();
|
||||
const resolvedPath = normalizeOptionalString(params.extensionPath);
|
||||
const extensionPath = resolvedPath ?? sqliteVec.getLoadablePath();
|
||||
|
||||
|
||||
@@ -134,6 +134,13 @@ export function createDeferred<T>() {
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
let proxyAgentConstructorPromise: Promise<typeof import("proxy-agent").ProxyAgent> | null = null;
|
||||
|
||||
async function loadProxyAgentConstructor(): Promise<typeof import("proxy-agent").ProxyAgent> {
|
||||
proxyAgentConstructorPromise ??= import("proxy-agent").then(({ ProxyAgent }) => ProxyAgent);
|
||||
return proxyAgentConstructorPromise;
|
||||
}
|
||||
|
||||
export async function resolveAmbientNodeProxyAgent<TAgent>(params?: {
|
||||
onError?: (error: unknown) => void;
|
||||
onUsingProxy?: () => void;
|
||||
@@ -143,7 +150,7 @@ export async function resolveAmbientNodeProxyAgent<TAgent>(params?: {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const { ProxyAgent } = await import("proxy-agent");
|
||||
const ProxyAgent = await loadProxyAgentConstructor();
|
||||
params?.onUsingProxy?.();
|
||||
return new ProxyAgent() as TAgent;
|
||||
} catch (error) {
|
||||
|
||||
@@ -34,6 +34,13 @@ type PtyModule = {
|
||||
|
||||
export type PtyAdapter = SpawnProcessAdapter;
|
||||
|
||||
let ptyModulePromise: Promise<PtyModule> | null = null;
|
||||
|
||||
async function loadPtyModule(): Promise<PtyModule> {
|
||||
ptyModulePromise ??= import("@lydell/node-pty") as Promise<unknown> as Promise<PtyModule>;
|
||||
return ptyModulePromise;
|
||||
}
|
||||
|
||||
export async function createPtyAdapter(params: {
|
||||
shell: string;
|
||||
args: string[];
|
||||
@@ -43,7 +50,7 @@ export async function createPtyAdapter(params: {
|
||||
rows?: number;
|
||||
name?: string;
|
||||
}): Promise<PtyAdapter> {
|
||||
const module = (await import("@lydell/node-pty")) as unknown as PtyModule;
|
||||
const module = await loadPtyModule();
|
||||
const spawn = module.spawn ?? module.default?.spawn;
|
||||
if (!spawn) {
|
||||
throw new Error("PTY support is unavailable (node-pty spawn not found).");
|
||||
|
||||
Reference in New Issue
Block a user