refactor: cache optional runtime imports

This commit is contained in:
Peter Steinberger
2026-04-18 20:43:11 +01:00
parent 6d40de45c7
commit 0195da6b0e
8 changed files with 93 additions and 21 deletions

View File

@@ -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,

View File

@@ -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;

View File

@@ -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 });