@@ -30,6 +30,52 @@ interface ParsedSemver {
3030const RETRYABLE_WRITE_ERRORS = new Set ( [ "EBUSY" , "EPERM" ] ) ;
3131let updateCacheWriteQueue : Promise < void > = Promise . resolve ( ) ;
3232
33+ function enqueueUpdateCacheWrite ( writeTask : ( ) => void ) : Promise < void > {
34+ const queued = updateCacheWriteQueue . catch ( ( ) => undefined ) . then ( writeTask ) ;
35+ updateCacheWriteQueue = queued . then (
36+ ( ) => undefined ,
37+ ( ) => undefined ,
38+ ) ;
39+ return queued ;
40+ }
41+
42+ function writeCacheContents ( serialized : string ) : void {
43+ let tempPath : string | null = null ;
44+ let wroteTemp = false ;
45+ try {
46+ if ( ! existsSync ( CACHE_DIR ) ) {
47+ mkdirSync ( CACHE_DIR , { recursive : true } ) ;
48+ }
49+ tempPath = `${ CACHE_FILE } .${ process . pid } .${ Date . now ( ) } .${ Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) } .tmp` ;
50+ let lastError : Error | null = null ;
51+ for ( let attempt = 0 ; attempt < 4 ; attempt ++ ) {
52+ try {
53+ writeFileSync ( tempPath , serialized , "utf8" ) ;
54+ renameSync ( tempPath , CACHE_FILE ) ;
55+ wroteTemp = false ;
56+ return ;
57+ } catch ( error ) {
58+ const code = ( error as NodeJS . ErrnoException ) . code ?? "" ;
59+ lastError = error as Error ;
60+ wroteTemp = true ;
61+ if ( ! RETRYABLE_WRITE_ERRORS . has ( code ) || attempt >= 3 ) {
62+ throw error ;
63+ }
64+ sleepSync ( 15 * ( 2 ** attempt ) ) ;
65+ }
66+ }
67+ if ( lastError ) throw lastError ;
68+ } finally {
69+ if ( wroteTemp && tempPath ) {
70+ try {
71+ unlinkSync ( tempPath ) ;
72+ } catch {
73+ // Best effort temp cleanup.
74+ }
75+ }
76+ }
77+ }
78+
3379function sleepSync ( ms : number ) : void {
3480 const delay = Math . max ( 0 , Math . floor ( ms ) ) ;
3581 if ( delay === 0 ) return ;
@@ -64,52 +110,13 @@ function loadCache(): UpdateCheckCache | null {
64110}
65111
66112async function saveCache ( cache : UpdateCheckCache ) : Promise < void > {
67- const writeTask = ( ) : void => {
68- let tempPath : string | null = null ;
69- let wroteTemp = false ;
113+ await enqueueUpdateCacheWrite ( ( ) => {
70114 try {
71- if ( ! existsSync ( CACHE_DIR ) ) {
72- mkdirSync ( CACHE_DIR , { recursive : true } ) ;
73- }
74- const serialized = JSON . stringify ( cache , null , 2 ) ;
75- tempPath = `${ CACHE_FILE } .${ process . pid } .${ Date . now ( ) } .${ Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) } .tmp` ;
76- let lastError : Error | null = null ;
77- for ( let attempt = 0 ; attempt < 4 ; attempt ++ ) {
78- try {
79- writeFileSync ( tempPath , serialized , "utf8" ) ;
80- renameSync ( tempPath , CACHE_FILE ) ;
81- wroteTemp = false ;
82- return ;
83- } catch ( error ) {
84- const code = ( error as NodeJS . ErrnoException ) . code ?? "" ;
85- lastError = error as Error ;
86- wroteTemp = true ;
87- if ( ! RETRYABLE_WRITE_ERRORS . has ( code ) || attempt >= 3 ) {
88- throw error ;
89- }
90- sleepSync ( 15 * ( 2 ** attempt ) ) ;
91- }
92- }
93- if ( lastError ) throw lastError ;
115+ writeCacheContents ( JSON . stringify ( cache , null , 2 ) ) ;
94116 } catch ( error ) {
95117 log . warn ( "Failed to save update cache" , { error : ( error as Error ) . message } ) ;
96- } finally {
97- if ( wroteTemp && tempPath ) {
98- try {
99- unlinkSync ( tempPath ) ;
100- } catch {
101- // Best effort temp cleanup.
102- }
103- }
104118 }
105- } ;
106-
107- const queued = updateCacheWriteQueue . catch ( ( ) => undefined ) . then ( writeTask ) ;
108- updateCacheWriteQueue = queued . then (
109- ( ) => undefined ,
110- ( ) => undefined ,
111- ) ;
112- await queued ;
119+ } ) ;
113120}
114121
115122function parseSemver ( version : string ) : ParsedSemver {
@@ -280,11 +287,13 @@ export async function checkAndNotify(
280287}
281288
282289export function clearUpdateCache ( ) : void {
283- try {
284- if ( existsSync ( CACHE_FILE ) ) {
285- writeFileSync ( CACHE_FILE , "{}" , "utf8" ) ;
286- }
287- } catch {
288- // Ignore errors
289- }
290+ void enqueueUpdateCacheWrite ( ( ) => {
291+ try {
292+ if ( existsSync ( CACHE_FILE ) ) {
293+ writeCacheContents ( "{}" ) ;
294+ }
295+ } catch {
296+ // Ignore errors
297+ }
298+ } ) ;
290299}
0 commit comments