From 3dfcf3be1d46b5f69db79a1282b2407984f67394 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:48:00 +0000 Subject: [PATCH 1/2] fix(util/jsonp): implement hybrid cleanup to fix memory leak This commit addresses a memory leak in the JSONP utility function where callback properties were accumulating on the global `window` object. Based on user feedback, a hybrid approach has been implemented: 1. On success: The callback property is completely deleted from the `window` object (with a fallback to `undefined`) to ensure no memory leak occurs for the most common case. 2. On timeout: The callback property is replaced with a `noop` function. This prevents console `TypeError` if a late-arriving response script attempts to call the callback after the timeout. Unit tests in `test/util/jsonp.test.ts` have been updated to verify this behavior, including a robust check for property removal on success. Co-authored-by: deflis <206113+deflis@users.noreply.github.com> --- src/util/jsonp.ts | 17 +++++++++++++---- test/util/jsonp.test.ts | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/util/jsonp.ts b/src/util/jsonp.ts index c76b824..291506f 100644 --- a/src/util/jsonp.ts +++ b/src/util/jsonp.ts @@ -75,14 +75,23 @@ export function jsonp( // リソース解放用の関数を定義 // スクリプトタグの削除、コールバック関数のクリーンアップ、タイマーのクリアを行う - const cleanup = function () { + const cleanup = function (isTimeout = false) { // Remove the script tag. if (script && script.parentNode) { script.parentNode.removeChild(script); } - // コールバック関数を空の関数に置き換えてメモリリークを防止 - window[id] = noop; + if (isTimeout) { + // タイムアウト時は遅延レスポンスによるコンソールエラーを避けるためにnoopを代入 + window[id] = noop; + } else { + // 成功時は完全に削除してメモリリークを防止 + try { + delete window[id]; + } catch (e) { + window[id] = undefined as any; + } + } if (timer) { clearTimeout(timer); @@ -94,7 +103,7 @@ export function jsonp( const timer = timeout > 0 ? setTimeout(() => { - cleanup(); + cleanup(true); reject(new Error("Timeout")); }, timeout) : undefined; diff --git a/test/util/jsonp.test.ts b/test/util/jsonp.test.ts index b30ed57..46c9cbd 100644 --- a/test/util/jsonp.test.ts +++ b/test/util/jsonp.test.ts @@ -130,6 +130,10 @@ describe('jsonp', () => { const result = await jsonpPromise; expect(result).toEqual(mockData); expect(mockParentNode.insertBefore).toHaveBeenCalledWith(mockScript, expect.anything()); + + // Check if callback is cleaned up (deleted on success) + expect(window[callbackName]).toBeUndefined(); + expect(callbackName in window).toBe(false); }); test('should apply custom options', async () => { @@ -161,6 +165,10 @@ describe('jsonp', () => { const result = await jsonpPromise; expect(result).toEqual(mockData); + + // Check if callback is cleaned up (deleted on success) + expect(window[callbackName]).toBeUndefined(); + expect(callbackName in window).toBe(false); }); test('should timeout and reject the promise', async () => { @@ -173,6 +181,12 @@ describe('jsonp', () => { vi.advanceTimersByTime(1001); await expect(promise).rejects.toThrow('Timeout'); + + // Check if callback is replaced by noop on timeout (not deleted) + const timeoutId = Object.keys(window).find(key => key.startsWith('__jp') && typeof window[key] === 'function'); + expect(timeoutId).toBeDefined(); + expect(typeof window[timeoutId!]).toBe('function'); + expect(timeoutId! in window).toBe(true); }); test('should use document.head if no script tags exist', async () => { From d789bf0639c075730fc447dd2303c589a5492c7d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:56:26 +0000 Subject: [PATCH 2/2] fix(util/jsonp): implement hybrid cleanup and fix lint error This commit addresses a memory leak in the JSONP utility function where callback properties were accumulating on the global `window` object. A hybrid approach has been implemented based on user requirements: - On success: The callback property is deleted from `window` to reclaim memory. - On timeout: The callback is replaced with a `noop` function to suppress errors from late-loading scripts. Additionally, a lint error was fixed by using optional catch binding (`catch { ... }`) for the `delete` fallback logic. Tests have been updated to verify property deletion on success and `noop` replacement on timeout. Co-authored-by: deflis <206113+deflis@users.noreply.github.com> --- src/util/jsonp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/jsonp.ts b/src/util/jsonp.ts index 291506f..d67c794 100644 --- a/src/util/jsonp.ts +++ b/src/util/jsonp.ts @@ -88,7 +88,7 @@ export function jsonp( // 成功時は完全に削除してメモリリークを防止 try { delete window[id]; - } catch (e) { + } catch { window[id] = undefined as any; } }