ace-interview-prep 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/commands/add.js +92 -0
  4. package/dist/commands/feedback.js +133 -0
  5. package/dist/commands/generate.js +224 -0
  6. package/dist/commands/init.js +100 -0
  7. package/dist/commands/list.js +107 -0
  8. package/dist/commands/reset.js +68 -0
  9. package/dist/commands/score.js +70 -0
  10. package/dist/commands/setup.js +84 -0
  11. package/dist/commands/test.js +85 -0
  12. package/dist/index.js +72 -0
  13. package/dist/lib/categories.js +103 -0
  14. package/dist/lib/config.js +61 -0
  15. package/dist/lib/llm.js +134 -0
  16. package/dist/lib/paths.js +38 -0
  17. package/dist/lib/scaffold.js +110 -0
  18. package/dist/lib/scorecard.js +116 -0
  19. package/dist/prompts/code-review.md +59 -0
  20. package/dist/prompts/design-review.md +67 -0
  21. package/dist/prompts/question-brainstorm.md +31 -0
  22. package/dist/prompts/question-generate.md +65 -0
  23. package/dist/templates/design/notes.md.hbs +27 -0
  24. package/dist/templates/js-ts/solution.test.ts.hbs +11 -0
  25. package/dist/templates/js-ts/solution.ts.hbs +11 -0
  26. package/dist/templates/leetcode-algo/solution.test.ts.hbs +11 -0
  27. package/dist/templates/leetcode-algo/solution.ts.hbs +11 -0
  28. package/dist/templates/leetcode-ds/solution.test.ts.hbs +11 -0
  29. package/dist/templates/leetcode-ds/solution.ts.hbs +11 -0
  30. package/dist/templates/react-apps/App.test.tsx.hbs +16 -0
  31. package/dist/templates/react-apps/App.tsx.hbs +16 -0
  32. package/dist/templates/readme.md.hbs +9 -0
  33. package/dist/templates/web-components/component.test.ts.hbs +11 -0
  34. package/dist/templates/web-components/component.ts.hbs +22 -0
  35. package/dist/templates/web-components/index.html.hbs +12 -0
  36. package/package.json +72 -0
  37. package/questions/design-be/url-shortener/README.md +23 -0
  38. package/questions/design-be/url-shortener/notes.md +27 -0
  39. package/questions/design-be/url-shortener/scorecard.json +1 -0
  40. package/questions/design-fe/news-feed/README.md +22 -0
  41. package/questions/design-fe/news-feed/notes.md +27 -0
  42. package/questions/design-fe/news-feed/scorecard.json +1 -0
  43. package/questions/design-full/google-docs/README.md +22 -0
  44. package/questions/design-full/google-docs/notes.md +27 -0
  45. package/questions/design-full/google-docs/scorecard.json +1 -0
  46. package/questions/js-ts/debounce/README.md +86 -0
  47. package/questions/js-ts/debounce/scorecard.json +9 -0
  48. package/questions/js-ts/debounce/solution.test.ts +128 -0
  49. package/questions/js-ts/debounce/solution.ts +4 -0
  50. package/questions/leetcode-algo/two-sum/README.md +58 -0
  51. package/questions/leetcode-algo/two-sum/scorecard.json +1 -0
  52. package/questions/leetcode-algo/two-sum/solution.test.ts +55 -0
  53. package/questions/leetcode-algo/two-sum/solution.ts +4 -0
  54. package/questions/leetcode-ds/lru-cache/README.md +70 -0
  55. package/questions/leetcode-ds/lru-cache/scorecard.json +1 -0
  56. package/questions/leetcode-ds/lru-cache/solution.test.ts +82 -0
  57. package/questions/leetcode-ds/lru-cache/solution.ts +14 -0
  58. package/questions/react-apps/todo-app/App.test.tsx +130 -0
  59. package/questions/react-apps/todo-app/App.tsx +10 -0
  60. package/questions/react-apps/todo-app/README.md +23 -0
  61. package/questions/react-apps/todo-app/scorecard.json +9 -0
  62. package/questions/web-components/star-rating/README.md +45 -0
  63. package/questions/web-components/star-rating/component.test.ts +64 -0
  64. package/questions/web-components/star-rating/component.ts +28 -0
  65. package/questions/web-components/star-rating/index.html +14 -0
  66. package/questions/web-components/star-rating/scorecard.json +9 -0
@@ -0,0 +1,11 @@
1
+ {{#if solutionCode}}
2
+ {{{solutionCode}}}
3
+ {{else}}
4
+ {{#if signature}}
5
+ {{{signature}}} {
6
+ // TODO: implement
7
+ }
8
+ {{else}}
9
+ // TODO: implement your algorithm here
10
+ {{/if}}
11
+ {{/if}}
@@ -0,0 +1,11 @@
1
+ {{#if testCode}}
2
+ {{{testCode}}}
3
+ {{else}}
4
+ import { describe, it, expect } from 'vitest';
5
+ // TODO: import your solution
6
+ // import { Solution } from './solution';
7
+
8
+ describe('{{title}}', () => {
9
+ it.todo('add test cases');
10
+ });
11
+ {{/if}}
@@ -0,0 +1,11 @@
1
+ {{#if solutionCode}}
2
+ {{{solutionCode}}}
3
+ {{else}}
4
+ {{#if signature}}
5
+ {{{signature}}} {
6
+ // TODO: implement
7
+ }
8
+ {{else}}
9
+ // TODO: implement your data structure here
10
+ {{/if}}
11
+ {{/if}}
@@ -0,0 +1,16 @@
1
+ {{#if testCode}}
2
+ {{{testCode}}}
3
+ {{else}}
4
+ import { describe, it, expect } from 'vitest';
5
+ import { render, screen } from '@testing-library/react';
6
+ import App from './App';
7
+
8
+ describe('{{title}}', () => {
9
+ it('renders without crashing', () => {
10
+ render(<App />);
11
+ expect(screen.getByText('{{title}}')).toBeDefined();
12
+ });
13
+
14
+ it.todo('add more test cases');
15
+ });
16
+ {{/if}}
@@ -0,0 +1,16 @@
1
+ {{#if solutionCode}}
2
+ {{{solutionCode}}}
3
+ {{else}}
4
+ import React from 'react';
5
+
6
+ // TODO: implement your React app here
7
+
8
+ export default function App() {
9
+ return (
10
+ <div>
11
+ <h1>{{title}}</h1>
12
+ {/* TODO: implement */}
13
+ </div>
14
+ );
15
+ }
16
+ {{/if}}
@@ -0,0 +1,9 @@
1
+ # {{title}}
2
+
3
+ **Category:** {{category}}
4
+ **Difficulty:** {{difficulty}}
5
+ **Suggested Time:** ~{{suggestedTime}} minutes
6
+
7
+ ---
8
+
9
+ {{{description}}}
@@ -0,0 +1,11 @@
1
+ {{#if testCode}}
2
+ {{{testCode}}}
3
+ {{else}}
4
+ import { describe, it, expect } from 'vitest';
5
+ // TODO: import your component
6
+ // import './component';
7
+
8
+ describe('{{title}}', () => {
9
+ it.todo('add test cases');
10
+ });
11
+ {{/if}}
@@ -0,0 +1,22 @@
1
+ {{#if solutionCode}}
2
+ {{{solutionCode}}}
3
+ {{else}}
4
+ {{#if signature}}
5
+ {{{signature}}}
6
+ {{else}}
7
+ // TODO: implement your web component here
8
+
9
+ export class MyComponent extends HTMLElement {
10
+ constructor() {
11
+ super();
12
+ this.attachShadow({ mode: 'open' });
13
+ }
14
+
15
+ connectedCallback() {
16
+ // TODO: implement
17
+ }
18
+ }
19
+
20
+ // customElements.define('my-component', MyComponent);
21
+ {{/if}}
22
+ {{/if}}
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{title}}</title>
7
+ </head>
8
+ <body>
9
+ <!-- TODO: use your web component here -->
10
+ <script type="module" src="./component.ts"></script>
11
+ </body>
12
+ </html>
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "ace-interview-prep",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool for frontend interview preparation — scaffolds questions with tests, tracks progress, and provides LLM-powered feedback",
5
+ "type": "module",
6
+ "bin": {
7
+ "ace": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist/",
11
+ "questions/",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "scripts": {
19
+ "build": "tsup && node scripts/postbuild.js",
20
+ "prepublishOnly": "npm run build",
21
+ "ace": "tsx cli/index.ts",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest"
24
+ },
25
+ "keywords": [
26
+ "interview",
27
+ "frontend",
28
+ "practice",
29
+ "cli",
30
+ "typescript",
31
+ "react",
32
+ "leetcode",
33
+ "system-design",
34
+ "web-components"
35
+ ],
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/neel/ace-interview-prep.git"
39
+ },
40
+ "homepage": "https://github.com/neel/ace-interview-prep#readme",
41
+ "bugs": {
42
+ "url": "https://github.com/neel/ace-interview-prep/issues"
43
+ },
44
+ "author": "Neel",
45
+ "license": "MIT",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "dependencies": {
50
+ "@anthropic-ai/sdk": "^0.74.0",
51
+ "chalk": "^5.6.2",
52
+ "cli-table3": "^0.6.5",
53
+ "dotenv": "^17.3.1",
54
+ "handlebars": "^4.7.8",
55
+ "openai": "^6.22.0",
56
+ "prompts": "^2.4.2"
57
+ },
58
+ "devDependencies": {
59
+ "@testing-library/jest-dom": "^6.9.1",
60
+ "@testing-library/react": "^16.3.2",
61
+ "@types/prompts": "^2.4.9",
62
+ "@types/react": "^19.2.14",
63
+ "@types/react-dom": "^19.2.3",
64
+ "happy-dom": "^20.6.1",
65
+ "react": "^19.2.4",
66
+ "react-dom": "^19.2.4",
67
+ "tsup": "^8.5.1",
68
+ "tsx": "^4.21.0",
69
+ "typescript": "^5.9.3",
70
+ "vitest": "^4.0.18"
71
+ }
72
+ }
@@ -0,0 +1,23 @@
1
+ # Design a URL Shortener
2
+
3
+ **Category:** Backend Design
4
+ **Difficulty:** Medium
5
+ **Suggested Time:** ~40 minutes
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ Design a URL shortening service (like bit.ly) from a **backend perspective**.
12
+
13
+ Consider the following aspects:
14
+
15
+ - **Encoding scheme** — How to generate short codes (base62, UUID, etc.)
16
+ - **Database design** — Schema for URLs, mappings, metadata
17
+ - **Read/write ratio** — Optimize for heavy read traffic
18
+ - **Caching** — Cache hot short URLs for fast redirects
19
+ - **Analytics** — Click tracking, geographic data
20
+ - **Expiration** — Optional TTL for short links
21
+ - **Horizontal scaling** — Sharding, load balancing
22
+
23
+ Walk through your design, data flow, and trade-offs. Use the `notes.md` file to capture your solution.
@@ -0,0 +1,27 @@
1
+ # Design a URL Shortener — Design Notes
2
+
3
+ ## Requirements
4
+
5
+ ### Functional Requirements
6
+ <!-- List the core features and user-facing requirements -->
7
+
8
+ ### Non-Functional Requirements
9
+ <!-- Performance, scalability, availability, security, etc. -->
10
+
11
+ ## High-Level Architecture
12
+ <!-- Describe the overall system architecture -->
13
+
14
+ ## Data Model
15
+ <!-- Define key data structures, schemas, etc. -->
16
+
17
+ ## API Design
18
+ <!-- Endpoint design, data contracts, etc. -->
19
+
20
+ ## Deep Dive
21
+ <!-- Pick 1-2 areas to go deep on -->
22
+
23
+ ## Scaling Considerations
24
+ <!-- How does this scale? -->
25
+
26
+ ## Trade-offs
27
+ <!-- What trade-offs did you make? -->
@@ -0,0 +1 @@
1
+ {"title":"Design a URL Shortener","category":"design-be","difficulty":"medium","suggestedTime":40,"status":"untouched","attempts":[],"llmFeedback":null}
@@ -0,0 +1,22 @@
1
+ # Design a News Feed
2
+
3
+ **Category:** Frontend Design
4
+ **Difficulty:** Hard
5
+ **Suggested Time:** ~55 minutes
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ Design a social media news feed (like Facebook or Twitter feed) from a **frontend perspective**.
12
+
13
+ Consider the following aspects:
14
+
15
+ - **Infinite scroll** — Loading more content as the user scrolls
16
+ - **Real-time updates** — New posts appearing without a full refresh
17
+ - **Optimistic UI** — Immediate feedback for likes, comments, shares
18
+ - **Caching** — Stale-while-revalidate, cache invalidation strategies
19
+ - **Virtualization** — Rendering only visible items for performance
20
+ - **Accessibility** — Screen readers, keyboard navigation, focus management
21
+
22
+ Walk through your design, data flow, and trade-offs. Use the `notes.md` file to capture your solution.
@@ -0,0 +1,27 @@
1
+ # Design a News Feed — Design Notes
2
+
3
+ ## Requirements
4
+
5
+ ### Functional Requirements
6
+ <!-- List the core features and user-facing requirements -->
7
+
8
+ ### Non-Functional Requirements
9
+ <!-- Performance, scalability, availability, security, etc. -->
10
+
11
+ ## High-Level Architecture
12
+ <!-- Describe the overall system architecture -->
13
+
14
+ ## Data Model
15
+ <!-- Define key data structures, schemas, etc. -->
16
+
17
+ ## API Design
18
+ <!-- Endpoint design, data contracts, etc. -->
19
+
20
+ ## Deep Dive
21
+ <!-- Pick 1-2 areas to go deep on -->
22
+
23
+ ## Scaling Considerations
24
+ <!-- How does this scale? -->
25
+
26
+ ## Trade-offs
27
+ <!-- What trade-offs did you make? -->
@@ -0,0 +1 @@
1
+ {"title":"Design a News Feed","category":"design-fe","difficulty":"hard","suggestedTime":55,"status":"untouched","attempts":[],"llmFeedback":null}
@@ -0,0 +1,22 @@
1
+ # Design Google Docs
2
+
3
+ **Category:** Full-Stack Design
4
+ **Difficulty:** Hard
5
+ **Suggested Time:** ~60 minutes
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ Design a collaborative document editor (like Google Docs) from a **full-stack perspective**.
12
+
13
+ Consider the following aspects:
14
+
15
+ - **Real-time collaboration** — Multiple users editing simultaneously
16
+ - **Conflict resolution** — Operational Transformation (OT) vs CRDTs
17
+ - **Document storage** — Versioning, persistence, recovery
18
+ - **Permissions** — View/edit access, sharing, roles
19
+ - **Offline support** — Edit offline, sync when reconnected
20
+ - **Cursor presence** — Showing other users' cursors and selections
21
+
22
+ Walk through your design, data flow, and trade-offs. Use the `notes.md` file to capture your solution.
@@ -0,0 +1,27 @@
1
+ # Design Google Docs — Design Notes
2
+
3
+ ## Requirements
4
+
5
+ ### Functional Requirements
6
+ <!-- List the core features and user-facing requirements -->
7
+
8
+ ### Non-Functional Requirements
9
+ <!-- Performance, scalability, availability, security, etc. -->
10
+
11
+ ## High-Level Architecture
12
+ <!-- Describe the overall system architecture -->
13
+
14
+ ## Data Model
15
+ <!-- Define key data structures, schemas, etc. -->
16
+
17
+ ## API Design
18
+ <!-- Endpoint design, data contracts, etc. -->
19
+
20
+ ## Deep Dive
21
+ <!-- Pick 1-2 areas to go deep on -->
22
+
23
+ ## Scaling Considerations
24
+ <!-- How does this scale? -->
25
+
26
+ ## Trade-offs
27
+ <!-- What trade-offs did you make? -->
@@ -0,0 +1 @@
1
+ {"title":"Design Google Docs","category":"design-full","difficulty":"hard","suggestedTime":60,"status":"untouched","attempts":[],"llmFeedback":null}
@@ -0,0 +1,86 @@
1
+ # Implement Debounce
2
+
3
+ **Category:** JS/TS Puzzles
4
+ **Difficulty:** Medium
5
+ **Suggested Time:** ~30 minutes
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ Implement a `debounce` function that delays invoking a function until after a specified `delay` (in milliseconds) has elapsed since the last time it was invoked. If the debounced function is called again before the delay expires, the timer resets.
12
+
13
+ Debouncing is commonly used for search inputs, resize handlers, scroll handlers, and other events that fire rapidly—ensuring the wrapped function only runs once after the user stops triggering it.
14
+
15
+ ## Function Signature
16
+
17
+ ```ts
18
+ function debounce(fn: (...args: any[]) => void, delay: number): (...args: any[]) => void
19
+ ```
20
+
21
+ - **`fn`** — The function to debounce. It may accept any arguments and returns `void`.
22
+ - **`delay`** — The debounce delay in milliseconds.
23
+ - **Returns** — A new debounced function with the same signature.
24
+
25
+ ## Examples
26
+
27
+ ### Example 1: Basic delay
28
+
29
+ ```ts
30
+ const log = (msg: string) => console.log(msg);
31
+ const debouncedLog = debounce(log, 100);
32
+
33
+ debouncedLog('hello'); // nothing logged yet
34
+ // ... 100ms passes ...
35
+ // logs: "hello"
36
+ ```
37
+
38
+ ### Example 2: Multiple rapid calls trigger only once
39
+
40
+ ```ts
41
+ const save = (data: string) => console.log('Saving:', data);
42
+ const debouncedSave = debounce(save, 200);
43
+
44
+ debouncedSave('a');
45
+ debouncedSave('b');
46
+ debouncedSave('c');
47
+ // ... 200ms passes ...
48
+ // logs: "Saving: c" (only the last call runs)
49
+ ```
50
+
51
+ ### Example 3: Timer resets on new calls
52
+
53
+ ```ts
54
+ const fn = vi.fn();
55
+ const debounced = debounce(fn, 100);
56
+
57
+ debounced();
58
+ // advance 50ms
59
+ debounced();
60
+ // advance 50ms (total 100ms since first call, but only 50ms since last)
61
+ // fn not called yet
62
+ // advance 50ms more (100ms since last call)
63
+ // fn called once
64
+ ```
65
+
66
+ ## Constraints
67
+
68
+ - The debounced function should pass through all arguments to the original function.
69
+ - The debounced function should preserve the `this` context when the original function is invoked.
70
+ - With `delay === 0`, the function should still debounce (i.e., schedule for the next tick / microtask boundary, or run immediately on the next call—implementation-dependent but should not fire synchronously on every call).
71
+ - The debounced function returns `void`; you do not need to return the original function's return value.
72
+
73
+ ## Expected Behavior
74
+
75
+ 1. **Basic delay** — The wrapped function is invoked only after `delay` ms of no further calls.
76
+ 2. **Rapid calls** — Multiple calls in quick succession result in a single invocation with the arguments from the last call.
77
+ 3. **Timer reset** — Each new call resets the timer; the function runs only after `delay` ms of inactivity.
78
+ 4. **Arguments** — All arguments passed to the debounced function are forwarded to the original function.
79
+ 5. **`this` context** — When the debounced function is called as a method, the original function receives the correct `this`.
80
+ 6. **Zero delay** — Behavior with `delay === 0` should still debounce (no synchronous double-invocation on rapid calls).
81
+
82
+ ## Hints
83
+
84
+ - Use `setTimeout` to schedule the invocation.
85
+ - Store the timeout ID so you can clear it when a new call comes in.
86
+ - Use `apply` or spread to pass arguments and preserve `this`.
@@ -0,0 +1,9 @@
1
+ {
2
+ "title": "Implement Debounce",
3
+ "category": "js-ts",
4
+ "difficulty": "medium",
5
+ "suggestedTime": 30,
6
+ "status": "untouched",
7
+ "attempts": [],
8
+ "llmFeedback": null
9
+ }
@@ -0,0 +1,128 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { debounce } from './solution';
3
+
4
+ describe('debounce', () => {
5
+ beforeEach(() => {
6
+ vi.useFakeTimers();
7
+ });
8
+
9
+ afterEach(() => {
10
+ vi.useRealTimers();
11
+ });
12
+
13
+ it('delays invocation by the specified delay', () => {
14
+ const fn = vi.fn();
15
+ const debounced = debounce(fn, 100);
16
+
17
+ debounced();
18
+ expect(fn).not.toHaveBeenCalled();
19
+
20
+ vi.advanceTimersByTime(99);
21
+ expect(fn).not.toHaveBeenCalled();
22
+
23
+ vi.advanceTimersByTime(1);
24
+ expect(fn).toHaveBeenCalledTimes(1);
25
+ });
26
+
27
+ it('invokes only once when called multiple times rapidly', () => {
28
+ const fn = vi.fn();
29
+ const debounced = debounce(fn, 100);
30
+
31
+ debounced();
32
+ debounced();
33
+ debounced();
34
+
35
+ vi.advanceTimersByTime(100);
36
+ expect(fn).toHaveBeenCalledTimes(1);
37
+ });
38
+
39
+ it('passes through all arguments to the original function', () => {
40
+ const fn = vi.fn();
41
+ const debounced = debounce(fn, 50);
42
+
43
+ debounced('a', 1, { key: 'value' });
44
+ vi.advanceTimersByTime(50);
45
+
46
+ expect(fn).toHaveBeenCalledWith('a', 1, { key: 'value' });
47
+ });
48
+
49
+ it('preserves this context when called as a method', () => {
50
+ const methodFn = vi.fn(function (this: { value: number }) {
51
+ return this.value;
52
+ });
53
+ const obj = {
54
+ value: 42,
55
+ method: methodFn,
56
+ };
57
+ obj.method = debounce(obj.method, 50);
58
+
59
+ obj.method();
60
+ vi.advanceTimersByTime(50);
61
+
62
+ expect(methodFn).toHaveBeenCalled();
63
+ expect(methodFn.mock.results[0]?.value).toBe(42);
64
+ });
65
+
66
+ it('resets timer when called again before delay expires', () => {
67
+ const fn = vi.fn();
68
+ const debounced = debounce(fn, 100);
69
+
70
+ debounced();
71
+ vi.advanceTimersByTime(50);
72
+ debounced();
73
+ vi.advanceTimersByTime(50);
74
+ expect(fn).not.toHaveBeenCalled();
75
+
76
+ vi.advanceTimersByTime(50);
77
+ expect(fn).toHaveBeenCalledTimes(1);
78
+ });
79
+
80
+ it('uses arguments from the last call when multiple rapid calls occur', () => {
81
+ const fn = vi.fn();
82
+ const debounced = debounce(fn, 100);
83
+
84
+ debounced('first');
85
+ debounced('second');
86
+ debounced('third');
87
+
88
+ vi.advanceTimersByTime(100);
89
+ expect(fn).toHaveBeenCalledTimes(1);
90
+ expect(fn).toHaveBeenCalledWith('third');
91
+ });
92
+
93
+ it('works with 0 delay (debounces to next tick)', () => {
94
+ const fn = vi.fn();
95
+ const debounced = debounce(fn, 0);
96
+
97
+ debounced();
98
+ debounced();
99
+ debounced();
100
+
101
+ vi.advanceTimersByTime(0);
102
+ expect(fn).toHaveBeenCalledTimes(1);
103
+ });
104
+
105
+ it('returns void (does not return the original function result)', () => {
106
+ const fn = vi.fn(() => 'result');
107
+ const debounced = debounce(fn, 50);
108
+
109
+ const result = debounced();
110
+ expect(result).toBeUndefined();
111
+
112
+ vi.advanceTimersByTime(50);
113
+ expect(fn).toHaveBeenCalled();
114
+ });
115
+
116
+ it('can be called again after the delay has fired', () => {
117
+ const fn = vi.fn();
118
+ const debounced = debounce(fn, 50);
119
+
120
+ debounced();
121
+ vi.advanceTimersByTime(50);
122
+ expect(fn).toHaveBeenCalledTimes(1);
123
+
124
+ debounced();
125
+ vi.advanceTimersByTime(50);
126
+ expect(fn).toHaveBeenCalledTimes(2);
127
+ });
128
+ });
@@ -0,0 +1,4 @@
1
+ export function debounce(fn: (...args: any[]) => void, delay: number): (...args: any[]) => void {
2
+ // TODO: implement
3
+ return (..._args: any[]) => {};
4
+ }
@@ -0,0 +1,58 @@
1
+ # Two Sum
2
+
3
+ **Category:** LeetCode Algorithms
4
+ **Difficulty:** Easy
5
+ **Suggested Time:** ~15 minutes
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ Given an array of integers `nums` and an integer `target`, return the indices of the two numbers that add up to `target`.
12
+
13
+ You may assume that each input has **exactly one solution**, and you may not use the same element twice. You can return the answer in any order.
14
+
15
+ ## Function Signature
16
+
17
+ ```ts
18
+ function twoSum(nums: number[], target: number): [number, number]
19
+ ```
20
+
21
+ - **`nums`** — Array of integers.
22
+ - **`target`** — The target sum.
23
+ - **Returns** — A tuple `[i, j]` where `nums[i] + nums[j] === target` and `i !== j`.
24
+
25
+ ## Examples
26
+
27
+ ### Example 1
28
+
29
+ ```ts
30
+ twoSum([2, 7, 11, 15], 9);
31
+ // returns [0, 1] because nums[0] + nums[1] = 2 + 7 = 9
32
+ ```
33
+
34
+ ### Example 2
35
+
36
+ ```ts
37
+ twoSum([3, 2, 4], 6);
38
+ // returns [1, 2] because nums[1] + nums[2] = 2 + 4 = 6
39
+ ```
40
+
41
+ ### Example 3
42
+
43
+ ```ts
44
+ twoSum([3, 3], 6);
45
+ // returns [0, 1]
46
+ ```
47
+
48
+ ## Constraints
49
+
50
+ - `2 <= nums.length <= 10^4`
51
+ - `-10^9 <= nums[i] <= 10^9`
52
+ - `-10^9 <= target <= 10^9`
53
+ - Only one valid answer exists.
54
+
55
+ ## Hints
56
+
57
+ - A brute-force approach checks every pair in O(n²).
58
+ - A hash map can store "complement" (target - current) for O(n) time and O(n) space.
@@ -0,0 +1 @@
1
+ {"title":"Two Sum","category":"leetcode-algo","difficulty":"easy","suggestedTime":15,"status":"untouched","attempts":[],"llmFeedback":null}