@@ -22,7 +22,8 @@ const LARK_ACCOUNTS_URL = "https://accounts.larksuite.com";
2222
2323const REGISTRATION_PATH = "/oauth/v1/app/registration" ;
2424
25- const REQUEST_TIMEOUT_MS = 10_000 ;
25+ /** Wizard/onboarding hang floor for registration guarded fetches (not FEISHU_HTTP_TIMEOUT_MS). */
26+ export const FEISHU_APP_REGISTRATION_TIMEOUT_MS = 10_000 ;
2627const DEFAULT_REGISTRATION_POLL_INTERVAL_SECONDS = 5 ;
2728const DEFAULT_REGISTRATION_EXPIRE_SECONDS = 600 ;
2829
@@ -57,6 +58,8 @@ type FeishuAppRegistrationFetchOptions = {
5758 fetchImpl ?: FeishuAppRegistrationFetch ;
5859 /** Override hostname lookup for hermetic SSRF-guard tests. */
5960 lookupFn ?: LookupFn ;
61+ /** Override the guarded-fetch hang floor (defaults to FEISHU_APP_REGISTRATION_TIMEOUT_MS). */
62+ timeoutMs ?: number ;
6063} ;
6164
6265interface RawBeginResponse {
@@ -105,11 +108,11 @@ async function postRegistration<T>(
105108 method : "POST" ,
106109 headers : { "Content-Type" : "application/x-www-form-urlencoded" } ,
107110 body : new URLSearchParams ( body ) . toString ( ) ,
108- signal : AbortSignal . timeout ( REQUEST_TIMEOUT_MS ) ,
109111 } ,
110112 auditContext : "feishu.app-registration.post" ,
111113 fetchImpl : options ?. fetchImpl ,
112114 lookupFn : options ?. lookupFn ,
115+ timeoutMs : options ?. timeoutMs ,
113116 } ) ;
114117}
115118
@@ -119,12 +122,18 @@ async function fetchFeishuJson<T>(params: {
119122 auditContext : string ;
120123 fetchImpl ?: FeishuAppRegistrationFetch ;
121124 lookupFn ?: LookupFn ;
125+ timeoutMs ?: number ;
122126} ) : Promise < T > {
127+ // Mirror streaming-card: pass timeoutMs into fetchWithSsrFGuard so undici
128+ // dispatcher + guarded abort both fire. Registration keeps the tighter
129+ // FEISHU_APP_REGISTRATION_TIMEOUT_MS wizard budget (not FEISHU_HTTP_TIMEOUT_MS).
130+ const timeoutMs = params . timeoutMs ?? FEISHU_APP_REGISTRATION_TIMEOUT_MS ;
123131 const { response, release } = await fetchWithSsrFGuard ( {
124132 url : params . url ,
125133 init : params . init ,
126134 fetchImpl : params . fetchImpl ,
127135 lookupFn : params . lookupFn ,
136+ timeoutMs,
128137 policy : { allowedHostnames : [ new URL ( params . url ) . hostname ] } ,
129138 auditContext : params . auditContext ,
130139 } ) ;
@@ -229,7 +238,7 @@ export async function pollAppRegistration(params: {
229238 const expireInMs =
230239 finiteSecondsToTimerSafeMilliseconds ( expireIn ) ??
231240 finiteSecondsToTimerSafeMilliseconds ( DEFAULT_REGISTRATION_EXPIRE_SECONDS ) ??
232- REQUEST_TIMEOUT_MS ;
241+ FEISHU_APP_REGISTRATION_TIMEOUT_MS ;
233242 const deadline = Date . now ( ) + expireInMs ;
234243
235244 while ( Date . now ( ) < deadline ) {
@@ -342,7 +351,6 @@ export async function getAppOwnerOpenId(params: {
342351 method : "POST" ,
343352 headers : { "Content-Type" : "application/json" } ,
344353 body : JSON . stringify ( { app_id : params . appId , app_secret : params . appSecret } ) ,
345- signal : AbortSignal . timeout ( REQUEST_TIMEOUT_MS ) ,
346354 } ,
347355 auditContext : "feishu.app-registration.owner-token" ,
348356 fetchImpl : params . fetchImpl ,
@@ -369,7 +377,6 @@ export async function getAppOwnerOpenId(params: {
369377 Authorization : `Bearer ${ tokenData . tenant_access_token } ` ,
370378 "Content-Type" : "application/json" ,
371379 } ,
372- signal : AbortSignal . timeout ( REQUEST_TIMEOUT_MS ) ,
373380 } ,
374381 auditContext : "feishu.app-registration.owner-app" ,
375382 fetchImpl : params . fetchImpl ,
@@ -395,6 +402,6 @@ function sleepRegistrationPollInterval(intervalSeconds: number): Promise<void> {
395402 const intervalMs =
396403 finiteSecondsToTimerSafeMilliseconds ( intervalSeconds ) ??
397404 finiteSecondsToTimerSafeMilliseconds ( DEFAULT_REGISTRATION_POLL_INTERVAL_SECONDS ) ??
398- REQUEST_TIMEOUT_MS ;
405+ FEISHU_APP_REGISTRATION_TIMEOUT_MS ;
399406 return sleep ( intervalMs ) ;
400407}
0 commit comments