This is an automated email from the ASF dual-hosted git repository. maximebeauchemin pushed a commit to branch js-to-ts in repository https://gitbox.apache.org/repos/asf/superset.git
commit 960a31f2117499168d7297ca4b307166c7cb2ef8 Author: Maxime Beauchemin <[email protected]> AuthorDate: Sun Sep 7 14:58:22 2025 -0700 feat(migration): complete DebouncedMessageQueue TypeScript migration - Migrate DebouncedMessageQueue.js to TypeScript with proper generics - Add DebouncedMessageQueueOptions interface for type-safe configuration - Implement proper class properties with private/readonly modifiers - CREATE missing test file: DebouncedMessageQueue.test.ts - All TypeScript compilation and tests pass - Improve js-to-ts command with test creation requirements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --- .claude/commands/js-to-ts.md | 14 ++++- .../src/utils/DebouncedMessageQueue.test.ts | 66 ++++++++++++++++++++++ ...cedMessageQueue.js => DebouncedMessageQueue.ts} | 31 ++++++++-- 3 files changed, 104 insertions(+), 7 deletions(-) diff --git a/.claude/commands/js-to-ts.md b/.claude/commands/js-to-ts.md index c34726faaf..36de748057 100644 --- a/.claude/commands/js-to-ts.md +++ b/.claude/commands/js-to-ts.md @@ -8,6 +8,7 @@ Atomically migrate a core JS/JSX file to TypeScript along with all related tests ``` - `<core-filename>` - Path to CORE file relative to `superset-frontend/` (e.g., `src/utils/common.js`, `src/middleware/loggerMiddleware.js`) - **CORE FILES ONLY**: No test files, mock files - agent will find and migrate related files automatically +- **Task Title**: Use core filename as task title (e.g., "DebouncedMessageQueue.js migration") --- @@ -54,13 +55,19 @@ find "$dirname" -name "${basename}.mock.js" **Migration Requirement:** All discovered related files MUST be migrated together as one atomic unit. +**Test File Creation:** If NO test files exist for the core file, CREATE a minimal test file using the following pattern: +- Location: Same directory as core file +- Name: `{basename}.test.ts` (e.g., `DebouncedMessageQueue.test.ts`) +- Content: Basic test structure importing and testing the main functionality +- Use proper TypeScript types in test file + ### Success Report Format ``` SUCCESS: Atomic Migration of {core-filename} ## Files Migrated (Atomic Unit) - Core: {core-filename} → {core-filename.ts/tsx} -- Tests: {list-of-test-files} → {list-of-test-files.ts/tsx} +- Tests: {list-of-test-files} → {list-of-test-files.ts/tsx} OR "CREATED: {basename}.test.ts" - Mocks: {list-of-mock-files} → {list-of-mock-files.ts} - Type files modified: {list-of-type-files} @@ -124,6 +131,11 @@ DEPENDENCY_BLOCK: Cannot migrate {filename} ## Coordinator Actions +### Task Creation (Coordinator) +When triggering the `/js-to-ts` command: +- **Task Title**: Use the core filename as the task title (e.g., "DebouncedMessageQueue.js migration", "hostNamesConfig.js migration") +- **Task Description**: Include the full relative path to help agent locate the file + ### Global Integration (Coordinator Only) When agents report `SUCCESS`: - Review agent's type improvements for consistency diff --git a/superset-frontend/src/utils/DebouncedMessageQueue.test.ts b/superset-frontend/src/utils/DebouncedMessageQueue.test.ts new file mode 100644 index 0000000000..2631d7c20c --- /dev/null +++ b/superset-frontend/src/utils/DebouncedMessageQueue.test.ts @@ -0,0 +1,66 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import DebouncedMessageQueue from './DebouncedMessageQueue'; + +describe('DebouncedMessageQueue', () => { + it('should create a queue with default options', () => { + const queue = new DebouncedMessageQueue(); + expect(queue).toBeDefined(); + expect(queue.trigger).toBeInstanceOf(Function); + }); + + it('should accept custom configuration options', () => { + const mockCallback = jest.fn(); + const queue = new DebouncedMessageQueue({ + callback: mockCallback, + sizeThreshold: 500, + delayThreshold: 2000, + }); + expect(queue).toBeDefined(); + }); + + it('should append items to the queue', () => { + const mockCallback = jest.fn(); + const queue = new DebouncedMessageQueue({ callback: mockCallback }); + + const testEvent = { id: 1, message: 'test' }; + queue.append(testEvent); + + // Verify the append method doesn't throw + expect(() => queue.append(testEvent)).not.toThrow(); + }); + + it('should handle generic types properly', () => { + interface TestEvent { + id: number; + data: string; + } + + const mockCallback = jest.fn(); + const queue = new DebouncedMessageQueue<TestEvent>({ + callback: mockCallback, + }); + + const testEvent: TestEvent = { id: 1, data: 'test' }; + queue.append(testEvent); + + expect(() => queue.append(testEvent)).not.toThrow(); + }); +}); diff --git a/superset-frontend/src/utils/DebouncedMessageQueue.js b/superset-frontend/src/utils/DebouncedMessageQueue.ts similarity index 69% rename from superset-frontend/src/utils/DebouncedMessageQueue.js rename to superset-frontend/src/utils/DebouncedMessageQueue.ts index 6fd6c5b778..99b50650ae 100644 --- a/superset-frontend/src/utils/DebouncedMessageQueue.js +++ b/superset-frontend/src/utils/DebouncedMessageQueue.ts @@ -18,26 +18,45 @@ */ import { debounce } from 'lodash'; -class DebouncedMessageQueue { +export interface DebouncedMessageQueueOptions<T> { + callback?: (events: T[]) => void; + sizeThreshold?: number; + delayThreshold?: number; +} + +class DebouncedMessageQueue<T = Record<string, unknown>> { + private queue: T[]; + + private readonly sizeThreshold: number; + + private readonly delayThreshold: number; + + private readonly callback: (events: T[]) => void; + + public readonly trigger: () => void; + constructor({ callback = () => {}, sizeThreshold = 1000, delayThreshold = 1000, - }) { + }: DebouncedMessageQueueOptions<T> = {}) { this.queue = []; this.sizeThreshold = sizeThreshold; this.delayThreshold = delayThreshold; - - this.trigger = debounce(this.trigger.bind(this), this.delayThreshold); this.callback = callback; + + this.trigger = debounce( + this.triggerInternal.bind(this), + this.delayThreshold, + ); } - append(eventData) { + append(eventData: T): void { this.queue.push(eventData); this.trigger(); } - trigger() { + private triggerInternal(): void { if (this.queue.length > 0) { const events = this.queue.splice(0, this.sizeThreshold); this.callback.call(null, events);
