diff --git a/src/entities/Font/model/store/appliedFontsStore/appliedFontsStore.svelte.ts b/src/entities/Font/model/store/appliedFontsStore/appliedFontsStore.svelte.ts index 34fe102..8b86f43 100644 --- a/src/entities/Font/model/store/appliedFontsStore/appliedFontsStore.svelte.ts +++ b/src/entities/Font/model/store/appliedFontsStore/appliedFontsStore.svelte.ts @@ -202,6 +202,11 @@ export class AppliedFontsManager { if (results[j].status === 'rejected') { const [key, config] = chunk[j]; const reason = (results[j] as PromiseRejectedResult).reason; + // Aborted fetches are not retriable failures — skip silently + const isAbort = reason instanceof FontFetchError + && reason.cause instanceof Error + && reason.cause.name === 'AbortError'; + if (isAbort) continue; if (reason instanceof FontFetchError) { console.error(`Font fetch failed: ${config.name}`, reason); } @@ -276,6 +281,7 @@ export class AppliedFontsManager { // Clean up remaining state this.#loadedFonts.delete(key); this.statuses.delete(key); + this.#eviction.remove(key); } } diff --git a/src/entities/Font/model/store/appliedFontsStore/fontEvictionPolicy/FontEvictionPolicy.test.ts b/src/entities/Font/model/store/appliedFontsStore/fontEvictionPolicy/FontEvictionPolicy.test.ts index 400a39f..6667c65 100644 --- a/src/entities/Font/model/store/appliedFontsStore/fontEvictionPolicy/FontEvictionPolicy.test.ts +++ b/src/entities/Font/model/store/appliedFontsStore/fontEvictionPolicy/FontEvictionPolicy.test.ts @@ -42,6 +42,23 @@ describe('FontEvictionPolicy', () => { expect(Array.from(policy.keys())).toEqual(expect.arrayContaining(['a@400', 'b@vf'])); }); + it('remove deletes key from tracking so it no longer appears in keys()', () => { + policy.touch('a@400', t0); + policy.touch('b@vf', t0); + policy.remove('a@400'); + expect(Array.from(policy.keys())).not.toContain('a@400'); + expect(Array.from(policy.keys())).toContain('b@vf'); + }); + + it('remove unpins the key so a subsequent touch + TTL would evict it', () => { + policy.touch('a@400', t0); + policy.pin('a@400'); + policy.remove('a@400'); + // re-touch and check it can be evicted again + policy.touch('a@400', t0); + expect(policy.shouldEvict('a@400', t0 + TTL)).toBe(true); + }); + it('clear resets all state', () => { policy.touch('a@400', t0); policy.pin('a@400'); diff --git a/src/entities/Font/model/store/appliedFontsStore/fontEvictionPolicy/FontEvictionPolicy.ts b/src/entities/Font/model/store/appliedFontsStore/fontEvictionPolicy/FontEvictionPolicy.ts index d692684..1a3a1b5 100644 --- a/src/entities/Font/model/store/appliedFontsStore/fontEvictionPolicy/FontEvictionPolicy.ts +++ b/src/entities/Font/model/store/appliedFontsStore/fontEvictionPolicy/FontEvictionPolicy.ts @@ -58,6 +58,12 @@ export class FontEvictionPolicy { return this.#usageTracker.keys(); } + /** Removes a font key from tracking. Called by the orchestrator after eviction. */ + remove(key: string): void { + this.#usageTracker.delete(key); + this.#pinnedFonts.delete(key); + } + /** Clears all usage timestamps and pinned keys. */ clear(): void { this.#usageTracker.clear();