perlsky is a Perl 5 implementation of an AT Protocol Personal Data Server.
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

Harden reusable browser smoke timeouts

alice bf1740db 012d7122

+25 -5
+25 -5
tools/browser-automation/dual-smoke.mjs
··· 29 29 notes: [], 30 30 }; 31 31 32 + if (config.accountSource) { 33 + summary.notes.push(`account source: ${config.accountSource}`); 34 + } 35 + 32 36 const AVATAR_PNG_BASE64 = 33 37 'iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAV0lEQVR4nO3PQQ0AIBDAMMC/58MCP7KkVbDX1pk5A6gWUC2gWkC1gGoB1QKqBVQLqBZQLaBaQLWAagHVAqoFVAuoFlAtoFpAtYBqAdUCqgVUC6gWUC2gWkD1B4a2AX/y3CvgAAAAAElFTkSuQmCC'; 34 38 ··· 252 256 }; 253 257 254 258 const fetchJson = async (url, options = {}) => { 255 - const res = await fetch(url, options); 259 + const timeoutMs = options.timeoutMs ?? 30000; 260 + const controller = new AbortController(); 261 + const timer = setTimeout(() => controller.abort(), timeoutMs); 262 + const fetchOptions = { 263 + ...options, 264 + signal: controller.signal, 265 + }; 266 + delete fetchOptions.timeoutMs; 267 + let res; 268 + try { 269 + res = await fetch(url, fetchOptions); 270 + } finally { 271 + clearTimeout(timer); 272 + } 256 273 const text = await res.text(); 257 274 let json; 258 275 try { ··· 270 287 return { ok: res.ok, status: res.status, url: res.url }; 271 288 }; 272 289 273 - const xrpcJson = async (nsid, { method = 'GET', token, params, body } = {}) => { 290 + const xrpcJson = async (nsid, { method = 'GET', token, params, body, timeoutMs } = {}) => { 274 291 const url = new URL(`${config.pdsUrl}/xrpc/${nsid}`); 275 292 if (params) { 276 293 for (const [key, value] of Object.entries(params)) { ··· 287 304 return fetchJson(url.toString(), { 288 305 method, 289 306 headers, 307 + timeoutMs, 290 308 body: body === undefined ? undefined : JSON.stringify(body), 291 309 }); 292 310 }; ··· 355 373 return result.json; 356 374 }; 357 375 358 - const pollNotifications = async ({ account, authorHandle, reasons, minIndexedAt }) => { 376 + const pollNotifications = async ({ account, authorHandle, reasons, minIndexedAt, timeoutMs = 180000 }) => { 359 377 const started = Date.now(); 360 378 let last; 361 - while (Date.now() - started < 180000) { 379 + while (Date.now() - started < timeoutMs) { 362 380 last = await xrpcJson('app.bsky.notification.listNotifications', { 363 381 token: account.accessJwt, 364 382 params: { limit: '100' }, 383 + timeoutMs: 15000, 365 384 }); 366 385 if (last.ok && Array.isArray(last.json?.notifications)) { 367 386 const matching = last.json.notifications.filter((item) => { ··· 385 404 await sleep(5000); 386 405 } 387 406 throw new Error( 388 - `notifications not observed for ${account.handle}; last status=${last?.status ?? 'none'} body=${last?.text ?? ''}`, 407 + `notifications not observed for ${account.handle} within ${timeoutMs}ms; last status=${last?.status ?? 'none'} body=${last?.text ?? ''}`, 389 408 ); 390 409 }; 391 410 ··· 1044 1063 authorHandle: secondary.handle, 1045 1064 reasons: ['follow'], 1046 1065 minIndexedAt: secondaryWaveStarted, 1066 + timeoutMs: 30000, 1047 1067 }); 1048 1068 return { 1049 1069 reasons: result.notifications.map((item) => item.reason),