leetcode-capture-helper 0.0.0-stage → 0.1.1

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 (48) hide show
  1. package/cli.js +92 -0
  2. package/config.js +25 -0
  3. package/framing.js +57 -0
  4. package/host.js +420 -0
  5. package/install.js +290 -0
  6. package/package.json +31 -4
  7. package/runtime/bundle.json +42 -0
  8. package/runtime/companion/capture-log.js +83 -0
  9. package/runtime/companion/codex-backend.js +337 -0
  10. package/runtime/companion/companion.js +2190 -0
  11. package/runtime/companion/mouse-input.js +154 -0
  12. package/runtime/companion/package.json +4 -0
  13. package/runtime/companion/panel-bridge.js +273 -0
  14. package/runtime/companion/settings-rpc.js +228 -0
  15. package/runtime/companion/settings.js +464 -0
  16. package/runtime/companion/terminal-format.js +330 -0
  17. package/runtime/companion/vault-summary.js +168 -0
  18. package/runtime/companion/virtual-terminal.js +138 -0
  19. package/runtime/extension/shared/anthropic-backend.js +193 -0
  20. package/runtime/extension/shared/catalog.js +415 -0
  21. package/runtime/extension/shared/code-ledger.js +333 -0
  22. package/runtime/extension/shared/context-budget.js +259 -0
  23. package/runtime/extension/shared/context-summary.js +271 -0
  24. package/runtime/extension/shared/conversation.js +81 -0
  25. package/runtime/extension/shared/event-hub.js +126 -0
  26. package/runtime/extension/shared/extension-settings.js +445 -0
  27. package/runtime/extension/shared/helper-launcher.js +248 -0
  28. package/runtime/extension/shared/local-backend.js +706 -0
  29. package/runtime/extension/shared/local-server.js +149 -0
  30. package/runtime/extension/shared/package.json +4 -0
  31. package/runtime/extension/shared/practice.js +228 -0
  32. package/runtime/extension/shared/problem-index.js +4128 -0
  33. package/runtime/extension/shared/profiles.js +106 -0
  34. package/runtime/extension/shared/providers.js +58 -0
  35. package/runtime/extension/shared/reply-guard.js +864 -0
  36. package/runtime/extension/shared/roadmap.js +1337 -0
  37. package/runtime/extension/shared/sanitize.js +68 -0
  38. package/runtime/extension/shared/solved.js +316 -0
  39. package/runtime/extension/shared/tutor-engine.js +369 -0
  40. package/runtime/extension/shared/tutor-prompt.js +108 -0
  41. package/runtime/extension/shared/tutor-service.js +520 -0
  42. package/runtime/relay-server/package.json +4 -0
  43. package/runtime/relay-server/panel-broker.js +797 -0
  44. package/runtime/relay-server/server.js +241 -0
  45. package/runtime/vault-tool/log-session.js +230 -0
  46. package/runtime/vault-tool/package.json +4 -0
  47. package/runtime/vault-tool/vault-notes.js +206 -0
  48. package/README.md +0 -3
@@ -0,0 +1,1337 @@
1
+ // The problem lists the Progress tab and the next-problem suggestions follow,
2
+ // plus the pure logic they share. No Node or browser APIs, so the options
3
+ // page, the background worker and the companion can all import it.
4
+ //
5
+ // A list is { id, title, short, kind, topics, edges, subjects }:
6
+ // topics its groups in the publisher's order, each { id, title, problems }
7
+ // where a problem is { slug, title, difficulty, topic } (title and
8
+ // difficulty come from the one shared catalog, shared/catalog.js)
9
+ // edges prerequisite lines [from, to] between topics; only NeetCode 150
10
+ // and NeetCode 250 have them (kind 'graph', one roadmap for both),
11
+ // every other list is drawn as ordered groups (kind 'groups') and
12
+ // its next group is simply the first one not finished
13
+ // subjects only for a list whose own groups are not subjects (Grind 75's
14
+ // weeks): a second grouping of the same problems by subject, each
15
+ // { id, title, problems } with the problems in the list's order;
16
+ // every problem then also carries `subject`. The list's order (and
17
+ // so what comes next) stays the publisher's; subjects are a view.
18
+ // The slug is LeetCode's own (leetcode.com/problems/<slug>/), which is also
19
+ // what the extension reads from the page address, so a problem solved once
20
+ // counts in every list.
21
+
22
+ import { CATALOG } from './catalog.js';
23
+
24
+ const words = (slugs) => slugs.split(/\s+/).filter(Boolean);
25
+
26
+ // Each group: [id, title, slugs], the slugs one per line in the list's order.
27
+ function defineList({ id, title, short, groupNoun, groups, edges = [], subjects = null }) {
28
+ const topics = groups.map(([topicId, topicTitle, slugs]) => ({
29
+ id: topicId,
30
+ title: topicTitle,
31
+ problems: words(slugs).map((slug) => {
32
+ const entry = CATALOG.get(slug);
33
+ if (!entry) throw new Error(`${id}: ${slug} is not in the catalog`);
34
+ return { slug, title: entry.title, difficulty: entry.difficulty, topic: topicId };
35
+ }),
36
+ }));
37
+ const size = topics.reduce((n, t) => n + t.problems.length, 0);
38
+ const list = { id, title, short: short || title, groupNoun, kind: edges.length ? 'graph' : 'groups', size, topics, edges, subjects: null };
39
+ if (subjects) list.subjects = defineSubjects(list, subjects);
40
+ return list;
41
+ }
42
+
43
+ // Every problem of the list in exactly one subject; each subject keeps the
44
+ // list's order.
45
+ function defineSubjects(list, subjects) {
46
+ const problems = list.topics.flatMap((t) => t.problems);
47
+ const where = new Map();
48
+ for (const [subjectId, , slugs] of subjects) {
49
+ for (const slug of words(slugs)) {
50
+ if (where.has(slug)) throw new Error(`${list.id}: ${slug} has two subjects`);
51
+ where.set(slug, subjectId);
52
+ }
53
+ }
54
+ for (const p of problems) {
55
+ if (!where.has(p.slug)) throw new Error(`${list.id}: ${p.slug} has no subject`);
56
+ p.subject = where.get(p.slug);
57
+ }
58
+ if (where.size !== problems.length) throw new Error(`${list.id}: a subject names a problem the list does not have`);
59
+ return subjects.map(([subjectId, subjectTitle]) => ({ id: subjectId, title: subjectTitle, problems: problems.filter((p) => p.subject === subjectId) }));
60
+ }
61
+
62
+ // NeetCode 150, in NeetCode's order, with its roadmap's prerequisite lines.
63
+ // Membership, topics and the order inside each topic are neetcode.io's own
64
+ // list data (https://neetcode.io/practice/practice/neetcode150, the problem
65
+ // objects in its main bundle, fetched 2026-11-04: e2e/neetcode-lists.txt,
66
+ // which roadmap.test.mjs compares). The older copy in the neetcode-gh/leetcode
67
+ // repository (.problemSiteData.json) still has Generate Parentheses under
68
+ // Stack; the site now lists it under Backtracking.
69
+ // The roadmap's own prerequisite lines: [from, to].
70
+ const EDGES = [
71
+ ['arrays-hashing', 'two-pointers'],
72
+ ['arrays-hashing', 'stack'],
73
+ ['two-pointers', 'binary-search'],
74
+ ['two-pointers', 'sliding-window'],
75
+ ['two-pointers', 'linked-list'],
76
+ ['binary-search', 'trees'],
77
+ ['linked-list', 'trees'],
78
+ ['trees', 'tries'],
79
+ ['trees', 'heap'],
80
+ ['trees', 'backtracking'],
81
+ ['backtracking', 'graphs'],
82
+ ['backtracking', 'dp-1d'],
83
+ ['heap', 'intervals'],
84
+ ['heap', 'greedy'],
85
+ ['heap', 'advanced-graphs'],
86
+ ['graphs', 'advanced-graphs'],
87
+ ['graphs', 'dp-2d'],
88
+ ['dp-1d', 'dp-2d'],
89
+ ['dp-1d', 'bit-manipulation'],
90
+ ['dp-2d', 'math-geometry'],
91
+ ['bit-manipulation', 'math-geometry'],
92
+ ];
93
+
94
+ export const NEETCODE_150 = defineList({
95
+ id: 'neetcode-150',
96
+ title: 'NeetCode 150',
97
+ groupNoun: 'topic',
98
+ edges: EDGES,
99
+ groups: [
100
+ ['arrays-hashing', 'Arrays & Hashing', `
101
+ contains-duplicate
102
+ valid-anagram
103
+ two-sum
104
+ group-anagrams
105
+ top-k-frequent-elements
106
+ encode-and-decode-strings
107
+ product-of-array-except-self
108
+ valid-sudoku
109
+ longest-consecutive-sequence`],
110
+ ['two-pointers', 'Two Pointers', `
111
+ valid-palindrome
112
+ two-sum-ii-input-array-is-sorted
113
+ 3sum
114
+ container-with-most-water
115
+ trapping-rain-water`],
116
+ ['sliding-window', 'Sliding Window', `
117
+ best-time-to-buy-and-sell-stock
118
+ longest-substring-without-repeating-characters
119
+ longest-repeating-character-replacement
120
+ permutation-in-string
121
+ minimum-window-substring
122
+ sliding-window-maximum`],
123
+ ['stack', 'Stack', `
124
+ valid-parentheses
125
+ min-stack
126
+ evaluate-reverse-polish-notation
127
+ daily-temperatures
128
+ car-fleet
129
+ largest-rectangle-in-histogram`],
130
+ ['binary-search', 'Binary Search', `
131
+ binary-search
132
+ search-a-2d-matrix
133
+ koko-eating-bananas
134
+ find-minimum-in-rotated-sorted-array
135
+ search-in-rotated-sorted-array
136
+ time-based-key-value-store
137
+ median-of-two-sorted-arrays`],
138
+ ['linked-list', 'Linked List', `
139
+ reverse-linked-list
140
+ merge-two-sorted-lists
141
+ linked-list-cycle
142
+ reorder-list
143
+ remove-nth-node-from-end-of-list
144
+ copy-list-with-random-pointer
145
+ add-two-numbers
146
+ find-the-duplicate-number
147
+ lru-cache
148
+ merge-k-sorted-lists
149
+ reverse-nodes-in-k-group`],
150
+ ['trees', 'Trees', `
151
+ invert-binary-tree
152
+ maximum-depth-of-binary-tree
153
+ diameter-of-binary-tree
154
+ balanced-binary-tree
155
+ same-tree
156
+ subtree-of-another-tree
157
+ lowest-common-ancestor-of-a-binary-search-tree
158
+ binary-tree-level-order-traversal
159
+ binary-tree-right-side-view
160
+ count-good-nodes-in-binary-tree
161
+ validate-binary-search-tree
162
+ kth-smallest-element-in-a-bst
163
+ construct-binary-tree-from-preorder-and-inorder-traversal
164
+ binary-tree-maximum-path-sum
165
+ serialize-and-deserialize-binary-tree`],
166
+ ['heap', 'Heap / Priority Queue', `
167
+ kth-largest-element-in-a-stream
168
+ last-stone-weight
169
+ k-closest-points-to-origin
170
+ kth-largest-element-in-an-array
171
+ task-scheduler
172
+ design-twitter
173
+ find-median-from-data-stream`],
174
+ ['backtracking', 'Backtracking', `
175
+ subsets
176
+ combination-sum
177
+ combination-sum-ii
178
+ permutations
179
+ subsets-ii
180
+ generate-parentheses
181
+ word-search
182
+ palindrome-partitioning
183
+ letter-combinations-of-a-phone-number
184
+ n-queens`],
185
+ ['tries', 'Tries', `
186
+ implement-trie-prefix-tree
187
+ design-add-and-search-words-data-structure
188
+ word-search-ii`],
189
+ ['graphs', 'Graphs', `
190
+ number-of-islands
191
+ max-area-of-island
192
+ clone-graph
193
+ walls-and-gates
194
+ rotting-oranges
195
+ pacific-atlantic-water-flow
196
+ surrounded-regions
197
+ course-schedule
198
+ course-schedule-ii
199
+ graph-valid-tree
200
+ number-of-connected-components-in-an-undirected-graph
201
+ redundant-connection
202
+ word-ladder`],
203
+ ['advanced-graphs', 'Advanced Graphs', `
204
+ network-delay-time
205
+ reconstruct-itinerary
206
+ min-cost-to-connect-all-points
207
+ swim-in-rising-water
208
+ alien-dictionary
209
+ cheapest-flights-within-k-stops`],
210
+ ['dp-1d', '1-D Dynamic Programming', `
211
+ climbing-stairs
212
+ min-cost-climbing-stairs
213
+ house-robber
214
+ house-robber-ii
215
+ longest-palindromic-substring
216
+ palindromic-substrings
217
+ decode-ways
218
+ coin-change
219
+ maximum-product-subarray
220
+ word-break
221
+ longest-increasing-subsequence
222
+ partition-equal-subset-sum`],
223
+ ['dp-2d', '2-D Dynamic Programming', `
224
+ unique-paths
225
+ longest-common-subsequence
226
+ best-time-to-buy-and-sell-stock-with-cooldown
227
+ coin-change-ii
228
+ target-sum
229
+ interleaving-string
230
+ longest-increasing-path-in-a-matrix
231
+ distinct-subsequences
232
+ edit-distance
233
+ burst-balloons
234
+ regular-expression-matching`],
235
+ ['greedy', 'Greedy', `
236
+ maximum-subarray
237
+ jump-game
238
+ jump-game-ii
239
+ gas-station
240
+ hand-of-straights
241
+ merge-triplets-to-form-target-triplet
242
+ partition-labels
243
+ valid-parenthesis-string`],
244
+ ['intervals', 'Intervals', `
245
+ insert-interval
246
+ merge-intervals
247
+ non-overlapping-intervals
248
+ meeting-rooms
249
+ meeting-rooms-ii
250
+ minimum-interval-to-include-each-query`],
251
+ ['bit-manipulation', 'Bit Manipulation', `
252
+ single-number
253
+ number-of-1-bits
254
+ counting-bits
255
+ reverse-bits
256
+ missing-number
257
+ sum-of-two-integers
258
+ reverse-integer`],
259
+ ['math-geometry', 'Math & Geometry', `
260
+ rotate-image
261
+ spiral-matrix
262
+ set-matrix-zeroes
263
+ happy-number
264
+ plus-one
265
+ powx-n
266
+ multiply-strings
267
+ detect-squares`],
268
+ ],
269
+ });
270
+
271
+ // NeetCode 250: NeetCode 150 plus 100 more, on the same roadmap (the same
272
+ // topics and prerequisite lines). Membership and the order inside each topic
273
+ // are neetcode.io's own list data (https://neetcode.io/practice/practice/neetcode250,
274
+ // the problem objects in its main bundle, fetched 2026-11-04:
275
+ // e2e/neetcode-lists.txt, which roadmap.test.mjs compares). The topics keep
276
+ // NeetCode 150's order (neetcode.io puts Math & Geometry before Bit
277
+ // Manipulation here, but the roadmap leads from Bit Manipulation to it).
278
+ export const NEETCODE_250 = defineList({
279
+ id: 'neetcode-250',
280
+ title: 'NeetCode 250',
281
+ groupNoun: 'topic',
282
+ edges: EDGES,
283
+ groups: [
284
+ ['arrays-hashing', 'Arrays & Hashing', `
285
+ concatenation-of-array
286
+ contains-duplicate
287
+ valid-anagram
288
+ two-sum
289
+ longest-common-prefix
290
+ group-anagrams
291
+ remove-element
292
+ majority-element
293
+ design-hashset
294
+ design-hashmap
295
+ sort-an-array
296
+ sort-colors
297
+ top-k-frequent-elements
298
+ encode-and-decode-strings
299
+ range-sum-query-2d-immutable
300
+ product-of-array-except-self
301
+ valid-sudoku
302
+ longest-consecutive-sequence
303
+ best-time-to-buy-and-sell-stock-ii
304
+ majority-element-ii
305
+ subarray-sum-equals-k
306
+ first-missing-positive`],
307
+ ['two-pointers', 'Two Pointers', `
308
+ reverse-string
309
+ valid-palindrome
310
+ valid-palindrome-ii
311
+ merge-strings-alternately
312
+ merge-sorted-array
313
+ remove-duplicates-from-sorted-array
314
+ two-sum-ii-input-array-is-sorted
315
+ 3sum
316
+ 4sum
317
+ rotate-array
318
+ container-with-most-water
319
+ boats-to-save-people
320
+ trapping-rain-water`],
321
+ ['sliding-window', 'Sliding Window', `
322
+ contains-duplicate-ii
323
+ best-time-to-buy-and-sell-stock
324
+ longest-substring-without-repeating-characters
325
+ longest-repeating-character-replacement
326
+ permutation-in-string
327
+ minimum-size-subarray-sum
328
+ find-k-closest-elements
329
+ minimum-window-substring
330
+ sliding-window-maximum`],
331
+ ['stack', 'Stack', `
332
+ baseball-game
333
+ valid-parentheses
334
+ implement-stack-using-queues
335
+ implement-queue-using-stacks
336
+ min-stack
337
+ evaluate-reverse-polish-notation
338
+ asteroid-collision
339
+ daily-temperatures
340
+ online-stock-span
341
+ car-fleet
342
+ simplify-path
343
+ decode-string
344
+ maximum-frequency-stack
345
+ largest-rectangle-in-histogram`],
346
+ ['binary-search', 'Binary Search', `
347
+ binary-search
348
+ search-insert-position
349
+ guess-number-higher-or-lower
350
+ sqrtx
351
+ search-a-2d-matrix
352
+ koko-eating-bananas
353
+ capacity-to-ship-packages-within-d-days
354
+ find-minimum-in-rotated-sorted-array
355
+ search-in-rotated-sorted-array
356
+ search-in-rotated-sorted-array-ii
357
+ time-based-key-value-store
358
+ split-array-largest-sum
359
+ median-of-two-sorted-arrays
360
+ find-in-mountain-array`],
361
+ ['linked-list', 'Linked List', `
362
+ reverse-linked-list
363
+ merge-two-sorted-lists
364
+ linked-list-cycle
365
+ reorder-list
366
+ remove-nth-node-from-end-of-list
367
+ copy-list-with-random-pointer
368
+ add-two-numbers
369
+ find-the-duplicate-number
370
+ reverse-linked-list-ii
371
+ design-circular-queue
372
+ lru-cache
373
+ lfu-cache
374
+ merge-k-sorted-lists
375
+ reverse-nodes-in-k-group`],
376
+ ['trees', 'Trees', `
377
+ binary-tree-inorder-traversal
378
+ binary-tree-preorder-traversal
379
+ binary-tree-postorder-traversal
380
+ invert-binary-tree
381
+ maximum-depth-of-binary-tree
382
+ diameter-of-binary-tree
383
+ balanced-binary-tree
384
+ same-tree
385
+ subtree-of-another-tree
386
+ lowest-common-ancestor-of-a-binary-search-tree
387
+ insert-into-a-binary-search-tree
388
+ delete-node-in-a-bst
389
+ binary-tree-level-order-traversal
390
+ binary-tree-right-side-view
391
+ construct-quad-tree
392
+ count-good-nodes-in-binary-tree
393
+ validate-binary-search-tree
394
+ kth-smallest-element-in-a-bst
395
+ construct-binary-tree-from-preorder-and-inorder-traversal
396
+ house-robber-iii
397
+ delete-leaves-with-a-given-value
398
+ binary-tree-maximum-path-sum
399
+ serialize-and-deserialize-binary-tree`],
400
+ ['heap', 'Heap / Priority Queue', `
401
+ kth-largest-element-in-a-stream
402
+ last-stone-weight
403
+ k-closest-points-to-origin
404
+ kth-largest-element-in-an-array
405
+ task-scheduler
406
+ design-twitter
407
+ single-threaded-cpu
408
+ reorganize-string
409
+ longest-happy-string
410
+ car-pooling
411
+ find-median-from-data-stream
412
+ ipo`],
413
+ ['backtracking', 'Backtracking', `
414
+ sum-of-all-subset-xor-totals
415
+ subsets
416
+ combination-sum
417
+ combination-sum-ii
418
+ combinations
419
+ permutations
420
+ subsets-ii
421
+ permutations-ii
422
+ generate-parentheses
423
+ word-search
424
+ palindrome-partitioning
425
+ letter-combinations-of-a-phone-number
426
+ matchsticks-to-square
427
+ partition-to-k-equal-sum-subsets
428
+ n-queens
429
+ n-queens-ii
430
+ word-break-ii`],
431
+ ['tries', 'Tries', `
432
+ implement-trie-prefix-tree
433
+ design-add-and-search-words-data-structure
434
+ extra-characters-in-a-string
435
+ word-search-ii`],
436
+ ['graphs', 'Graphs', `
437
+ island-perimeter
438
+ verifying-an-alien-dictionary
439
+ find-the-town-judge
440
+ number-of-islands
441
+ max-area-of-island
442
+ clone-graph
443
+ walls-and-gates
444
+ rotting-oranges
445
+ pacific-atlantic-water-flow
446
+ surrounded-regions
447
+ open-the-lock
448
+ course-schedule
449
+ course-schedule-ii
450
+ graph-valid-tree
451
+ course-schedule-iv
452
+ number-of-connected-components-in-an-undirected-graph
453
+ redundant-connection
454
+ accounts-merge
455
+ evaluate-division
456
+ minimum-height-trees
457
+ word-ladder`],
458
+ ['advanced-graphs', 'Advanced Graphs', `
459
+ path-with-minimum-effort
460
+ network-delay-time
461
+ reconstruct-itinerary
462
+ min-cost-to-connect-all-points
463
+ swim-in-rising-water
464
+ alien-dictionary
465
+ cheapest-flights-within-k-stops
466
+ find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree
467
+ build-a-matrix-with-conditions
468
+ greatest-common-divisor-traversal`],
469
+ ['dp-1d', '1-D Dynamic Programming', `
470
+ climbing-stairs
471
+ min-cost-climbing-stairs
472
+ n-th-tribonacci-number
473
+ house-robber
474
+ house-robber-ii
475
+ longest-palindromic-substring
476
+ palindromic-substrings
477
+ decode-ways
478
+ coin-change
479
+ maximum-product-subarray
480
+ word-break
481
+ longest-increasing-subsequence
482
+ partition-equal-subset-sum
483
+ combination-sum-iv
484
+ perfect-squares
485
+ integer-break
486
+ stone-game-iii`],
487
+ ['dp-2d', '2-D Dynamic Programming', `
488
+ unique-paths
489
+ unique-paths-ii
490
+ minimum-path-sum
491
+ longest-common-subsequence
492
+ last-stone-weight-ii
493
+ best-time-to-buy-and-sell-stock-with-cooldown
494
+ coin-change-ii
495
+ target-sum
496
+ interleaving-string
497
+ stone-game
498
+ stone-game-ii
499
+ longest-increasing-path-in-a-matrix
500
+ distinct-subsequences
501
+ edit-distance
502
+ burst-balloons
503
+ regular-expression-matching`],
504
+ ['greedy', 'Greedy', `
505
+ lemonade-change
506
+ maximum-subarray
507
+ maximum-sum-circular-subarray
508
+ longest-turbulent-subarray
509
+ jump-game
510
+ jump-game-ii
511
+ jump-game-vii
512
+ gas-station
513
+ hand-of-straights
514
+ dota2-senate
515
+ merge-triplets-to-form-target-triplet
516
+ partition-labels
517
+ valid-parenthesis-string
518
+ candy`],
519
+ ['intervals', 'Intervals', `
520
+ insert-interval
521
+ merge-intervals
522
+ non-overlapping-intervals
523
+ meeting-rooms
524
+ meeting-rooms-ii
525
+ meeting-rooms-iii
526
+ minimum-interval-to-include-each-query`],
527
+ ['bit-manipulation', 'Bit Manipulation', `
528
+ single-number
529
+ number-of-1-bits
530
+ counting-bits
531
+ add-binary
532
+ reverse-bits
533
+ missing-number
534
+ sum-of-two-integers
535
+ reverse-integer
536
+ bitwise-and-of-numbers-range
537
+ minimum-array-end`],
538
+ ['math-geometry', 'Math & Geometry', `
539
+ excel-sheet-column-title
540
+ greatest-common-divisor-of-strings
541
+ insert-greatest-common-divisors-in-linked-list
542
+ transpose-matrix
543
+ rotate-image
544
+ spiral-matrix
545
+ set-matrix-zeroes
546
+ happy-number
547
+ plus-one
548
+ roman-to-integer
549
+ powx-n
550
+ multiply-strings
551
+ detect-squares`],
552
+ ],
553
+ });
554
+
555
+ // The original Blind 75, exactly as the 2018 post lists it
556
+ // (https://www.teamblind.com/post/New-Year-Gift---Curated-List-of-Top-75-LeetCode-Questions-to-Save-Your-Time-OaM1orEU,
557
+ // saved as e2e/blind-75.txt by scripts/list-sources.mjs and compared by
558
+ // roadmap.test.mjs): its categories, their order and the order inside each.
559
+ // Merge k Sorted Lists is listed under both Linked List and Heap there; it is
560
+ // kept once, under Linked List, so the list has 75 distinct problems.
561
+ // neetcode.io's Blind 75 (its blind75 flag) has the same problems but one:
562
+ // the post's "Combination Sum" links to Combination Sum IV, which neetcode.io
563
+ // replaces with Combination Sum. The post's own link is kept here.
564
+ export const BLIND_75 = defineList({
565
+ id: 'blind-75',
566
+ title: 'Blind 75',
567
+ groupNoun: 'category',
568
+ groups: [
569
+ ['array', 'Array', `
570
+ two-sum
571
+ best-time-to-buy-and-sell-stock
572
+ contains-duplicate
573
+ product-of-array-except-self
574
+ maximum-subarray
575
+ maximum-product-subarray
576
+ find-minimum-in-rotated-sorted-array
577
+ search-in-rotated-sorted-array
578
+ 3sum
579
+ container-with-most-water`],
580
+ ['binary', 'Binary', `
581
+ sum-of-two-integers
582
+ number-of-1-bits
583
+ counting-bits
584
+ missing-number
585
+ reverse-bits`],
586
+ ['dynamic-programming', 'Dynamic Programming', `
587
+ climbing-stairs
588
+ coin-change
589
+ longest-increasing-subsequence
590
+ longest-common-subsequence
591
+ word-break
592
+ combination-sum-iv
593
+ house-robber
594
+ house-robber-ii
595
+ decode-ways
596
+ unique-paths
597
+ jump-game`],
598
+ ['graph', 'Graph', `
599
+ clone-graph
600
+ course-schedule
601
+ pacific-atlantic-water-flow
602
+ number-of-islands
603
+ longest-consecutive-sequence
604
+ alien-dictionary
605
+ graph-valid-tree
606
+ number-of-connected-components-in-an-undirected-graph`],
607
+ ['interval', 'Interval', `
608
+ insert-interval
609
+ merge-intervals
610
+ non-overlapping-intervals
611
+ meeting-rooms
612
+ meeting-rooms-ii`],
613
+ ['linked-list', 'Linked List', `
614
+ reverse-linked-list
615
+ linked-list-cycle
616
+ merge-two-sorted-lists
617
+ merge-k-sorted-lists
618
+ remove-nth-node-from-end-of-list
619
+ reorder-list`],
620
+ ['matrix', 'Matrix', `
621
+ set-matrix-zeroes
622
+ spiral-matrix
623
+ rotate-image
624
+ word-search`],
625
+ ['string', 'String', `
626
+ longest-substring-without-repeating-characters
627
+ longest-repeating-character-replacement
628
+ minimum-window-substring
629
+ valid-anagram
630
+ group-anagrams
631
+ valid-parentheses
632
+ valid-palindrome
633
+ longest-palindromic-substring
634
+ palindromic-substrings
635
+ encode-and-decode-strings`],
636
+ ['tree', 'Tree', `
637
+ maximum-depth-of-binary-tree
638
+ same-tree
639
+ invert-binary-tree
640
+ binary-tree-maximum-path-sum
641
+ binary-tree-level-order-traversal
642
+ serialize-and-deserialize-binary-tree
643
+ subtree-of-another-tree
644
+ construct-binary-tree-from-preorder-and-inorder-traversal
645
+ validate-binary-search-tree
646
+ kth-smallest-element-in-a-bst
647
+ lowest-common-ancestor-of-a-binary-search-tree
648
+ implement-trie-prefix-tree
649
+ design-add-and-search-words-data-structure
650
+ word-search-ii`],
651
+ ['heap', 'Heap', `
652
+ top-k-frequent-elements
653
+ find-median-from-data-stream`],
654
+ ],
655
+ });
656
+
657
+ // Grind 75 (Yangshun Tay, https://www.techinterviewhandbook.org/grind75) as
658
+ // the site shows it at its defaults: 8 weeks at 8 hours a week, ordered by
659
+ // difficulty, grouped by weeks. The site computes that from its own data (169
660
+ // questions): it takes questions by priority while 1.96 minutes per minute of
661
+ // each one's duration fit in 60 * 8 * 8, which gives exactly these 75, orders
662
+ // them Easy, Medium, Hard (keeping priority order inside each), and starts a
663
+ // new week once the next one would pass 60 * 8. Other settings change both
664
+ // the set and the weeks (12 weeks gives 111 questions), so this list is only
665
+ // the default. e2e/grind-75.txt is that result, written by
666
+ // scripts/list-sources.mjs, and roadmap.test.mjs compares it. The site still
667
+ // grades Lowest Common Ancestor of a Binary Search Tree Easy; LeetCode now
668
+ // says Medium, and the catalog keeps LeetCode's grade.
669
+ export const GRIND_75 = defineList({
670
+ id: 'grind-75',
671
+ title: 'Grind 75',
672
+ groupNoun: 'week',
673
+ groups: [
674
+ ['week-1', 'Week 1', `
675
+ two-sum
676
+ valid-parentheses
677
+ merge-two-sorted-lists
678
+ best-time-to-buy-and-sell-stock
679
+ valid-palindrome
680
+ invert-binary-tree
681
+ valid-anagram
682
+ binary-search
683
+ flood-fill
684
+ lowest-common-ancestor-of-a-binary-search-tree
685
+ balanced-binary-tree
686
+ linked-list-cycle
687
+ implement-queue-using-stacks`],
688
+ ['week-2', 'Week 2', `
689
+ first-bad-version
690
+ ransom-note
691
+ climbing-stairs
692
+ longest-palindrome
693
+ reverse-linked-list
694
+ majority-element
695
+ add-binary
696
+ diameter-of-binary-tree
697
+ middle-of-the-linked-list
698
+ maximum-depth-of-binary-tree
699
+ contains-duplicate
700
+ maximum-subarray`],
701
+ ['week-3', 'Week 3', `
702
+ insert-interval
703
+ 01-matrix
704
+ k-closest-points-to-origin
705
+ longest-substring-without-repeating-characters
706
+ 3sum
707
+ binary-tree-level-order-traversal
708
+ clone-graph
709
+ evaluate-reverse-polish-notation`],
710
+ ['week-4', 'Week 4', `
711
+ course-schedule
712
+ implement-trie-prefix-tree
713
+ coin-change
714
+ product-of-array-except-self
715
+ min-stack
716
+ validate-binary-search-tree
717
+ number-of-islands
718
+ rotting-oranges`],
719
+ ['week-5', 'Week 5', `
720
+ search-in-rotated-sorted-array
721
+ combination-sum
722
+ permutations
723
+ merge-intervals
724
+ lowest-common-ancestor-of-a-binary-tree
725
+ time-based-key-value-store
726
+ accounts-merge
727
+ sort-colors`],
728
+ ['week-6', 'Week 6', `
729
+ word-break
730
+ partition-equal-subset-sum
731
+ string-to-integer-atoi
732
+ spiral-matrix
733
+ subsets
734
+ binary-tree-right-side-view
735
+ longest-palindromic-substring
736
+ unique-paths
737
+ construct-binary-tree-from-preorder-and-inorder-traversal`],
738
+ ['week-7', 'Week 7', `
739
+ container-with-most-water
740
+ letter-combinations-of-a-phone-number
741
+ word-search
742
+ find-all-anagrams-in-a-string
743
+ minimum-height-trees
744
+ task-scheduler
745
+ lru-cache`],
746
+ ['week-8', 'Week 8', `
747
+ kth-smallest-element-in-a-bst
748
+ minimum-window-substring
749
+ serialize-and-deserialize-binary-tree
750
+ trapping-rain-water
751
+ find-median-from-data-stream
752
+ word-ladder
753
+ basic-calculator
754
+ maximum-profit-in-job-scheduling
755
+ merge-k-sorted-lists
756
+ largest-rectangle-in-histogram`],
757
+ ],
758
+ // Grind 75 groups by week only, so each problem also gets one subject, in
759
+ // NeetCode's topic names and order: the topic neetcode.io itself gives the
760
+ // problem (its NeetCode 150 topic, else its NeetCode 250 topic, else the
761
+ // topic in neetcode.io's full catalog; e2e/neetcode-lists.txt has all three,
762
+ // and roadmap.test.mjs checks the rule). Four are not on neetcode.io at all
763
+ // and follow their LeetCode tags (shared/problem-index.js): 01 Matrix (Array,
764
+ // Dynamic Programming, Matrix, Breadth-First Search) is Graphs, a grid BFS
765
+ // like Rotting Oranges; First Bad Version (Binary Search, Interactive) is
766
+ // Binary Search; String to Integer (atoi) (String only) is Arrays & Hashing,
767
+ // where NeetCode puts plain string problems; Basic Calculator (String, Math,
768
+ // Stack, Recursion) is Stack.
769
+ subjects: [
770
+ ['arrays-hashing', 'Arrays & Hashing', `
771
+ two-sum
772
+ valid-anagram
773
+ ransom-note
774
+ longest-palindrome
775
+ majority-element
776
+ contains-duplicate
777
+ product-of-array-except-self
778
+ sort-colors
779
+ string-to-integer-atoi
780
+ find-all-anagrams-in-a-string`],
781
+ ['two-pointers', 'Two Pointers', `
782
+ valid-palindrome
783
+ 3sum
784
+ container-with-most-water
785
+ trapping-rain-water`],
786
+ ['sliding-window', 'Sliding Window', `
787
+ best-time-to-buy-and-sell-stock
788
+ longest-substring-without-repeating-characters
789
+ minimum-window-substring`],
790
+ ['stack', 'Stack', `
791
+ valid-parentheses
792
+ implement-queue-using-stacks
793
+ evaluate-reverse-polish-notation
794
+ min-stack
795
+ basic-calculator
796
+ largest-rectangle-in-histogram`],
797
+ ['binary-search', 'Binary Search', `
798
+ binary-search
799
+ first-bad-version
800
+ search-in-rotated-sorted-array
801
+ time-based-key-value-store`],
802
+ ['linked-list', 'Linked List', `
803
+ merge-two-sorted-lists
804
+ linked-list-cycle
805
+ reverse-linked-list
806
+ middle-of-the-linked-list
807
+ lru-cache
808
+ merge-k-sorted-lists`],
809
+ ['trees', 'Trees', `
810
+ invert-binary-tree
811
+ lowest-common-ancestor-of-a-binary-search-tree
812
+ balanced-binary-tree
813
+ diameter-of-binary-tree
814
+ maximum-depth-of-binary-tree
815
+ binary-tree-level-order-traversal
816
+ validate-binary-search-tree
817
+ lowest-common-ancestor-of-a-binary-tree
818
+ binary-tree-right-side-view
819
+ construct-binary-tree-from-preorder-and-inorder-traversal
820
+ kth-smallest-element-in-a-bst
821
+ serialize-and-deserialize-binary-tree`],
822
+ ['heap', 'Heap / Priority Queue', `
823
+ k-closest-points-to-origin
824
+ task-scheduler
825
+ find-median-from-data-stream`],
826
+ ['backtracking', 'Backtracking', `
827
+ combination-sum
828
+ permutations
829
+ subsets
830
+ letter-combinations-of-a-phone-number
831
+ word-search`],
832
+ ['tries', 'Tries', `
833
+ implement-trie-prefix-tree`],
834
+ ['graphs', 'Graphs', `
835
+ flood-fill
836
+ 01-matrix
837
+ clone-graph
838
+ course-schedule
839
+ number-of-islands
840
+ rotting-oranges
841
+ accounts-merge
842
+ minimum-height-trees
843
+ word-ladder`],
844
+ ['dp-1d', '1-D Dynamic Programming', `
845
+ climbing-stairs
846
+ coin-change
847
+ word-break
848
+ partition-equal-subset-sum
849
+ longest-palindromic-substring
850
+ maximum-profit-in-job-scheduling`],
851
+ ['dp-2d', '2-D Dynamic Programming', `
852
+ unique-paths`],
853
+ ['greedy', 'Greedy', `
854
+ maximum-subarray`],
855
+ ['intervals', 'Intervals', `
856
+ insert-interval
857
+ merge-intervals`],
858
+ ['bit-manipulation', 'Bit Manipulation', `
859
+ add-binary`],
860
+ ['math-geometry', 'Math & Geometry', `
861
+ spiral-matrix`],
862
+ ],
863
+ });
864
+
865
+ // LeetCode's own Top 100 Liked study plan (plan top-100-liked), with its
866
+ // groups exactly as LeetCode's GraphQL gave them on 2026-11-04 (so Binary Tree
867
+ // Maximum Path Sum sits under Binary Search, as LeetCode has it), checked
868
+ // against e2e/leetcode-study-plans.txt by roadmap.test.mjs.
869
+ export const TOP_100_LIKED = defineList({
870
+ id: 'top-100-liked',
871
+ title: 'Top 100 Liked by LeetCode',
872
+ groupNoun: 'topic',
873
+ groups: [
874
+ ['backtracking', 'Backtracking', `
875
+ letter-combinations-of-a-phone-number
876
+ generate-parentheses
877
+ combination-sum
878
+ permutations
879
+ n-queens
880
+ subsets
881
+ word-search
882
+ palindrome-partitioning`],
883
+ ['binary-search', 'Binary Search', `
884
+ median-of-two-sorted-arrays
885
+ search-in-rotated-sorted-array
886
+ find-first-and-last-position-of-element-in-sorted-array
887
+ search-insert-position
888
+ search-a-2d-matrix
889
+ binary-tree-maximum-path-sum
890
+ find-minimum-in-rotated-sorted-array`],
891
+ ['binary-tree', 'Binary Tree', `
892
+ binary-tree-inorder-traversal
893
+ validate-binary-search-tree
894
+ symmetric-tree
895
+ binary-tree-level-order-traversal
896
+ maximum-depth-of-binary-tree
897
+ construct-binary-tree-from-preorder-and-inorder-traversal
898
+ convert-sorted-array-to-binary-search-tree
899
+ flatten-binary-tree-to-linked-list
900
+ binary-tree-right-side-view
901
+ invert-binary-tree
902
+ kth-smallest-element-in-a-bst
903
+ lowest-common-ancestor-of-a-binary-tree
904
+ path-sum-iii
905
+ diameter-of-binary-tree`],
906
+ ['dynamic-programming', 'Dynamic Programming', `
907
+ longest-palindromic-substring
908
+ longest-valid-parentheses
909
+ unique-paths
910
+ minimum-path-sum
911
+ climbing-stairs
912
+ edit-distance
913
+ pascals-triangle
914
+ word-break
915
+ maximum-product-subarray
916
+ house-robber
917
+ perfect-squares
918
+ longest-increasing-subsequence
919
+ coin-change
920
+ partition-equal-subset-sum
921
+ longest-common-subsequence`],
922
+ ['graph', 'Graph', `
923
+ number-of-islands
924
+ course-schedule
925
+ rotting-oranges`],
926
+ ['greedy', 'Greedy', `
927
+ jump-game-ii
928
+ jump-game
929
+ best-time-to-buy-and-sell-stock
930
+ partition-labels`],
931
+ ['hashing', 'Hashing', `
932
+ two-sum
933
+ group-anagrams
934
+ longest-consecutive-sequence
935
+ subarray-sum-equals-k`],
936
+ ['heap', 'Heap', `
937
+ kth-largest-element-in-an-array
938
+ find-median-from-data-stream
939
+ top-k-frequent-elements`],
940
+ ['linked-lists', 'Linked Lists', `
941
+ add-two-numbers
942
+ remove-nth-node-from-end-of-list
943
+ merge-two-sorted-lists
944
+ merge-k-sorted-lists
945
+ swap-nodes-in-pairs
946
+ reverse-nodes-in-k-group
947
+ copy-list-with-random-pointer
948
+ linked-list-cycle
949
+ linked-list-cycle-ii
950
+ lru-cache
951
+ sort-list
952
+ intersection-of-two-linked-lists
953
+ reverse-linked-list
954
+ palindrome-linked-list`],
955
+ ['matrix', 'Matrix', `
956
+ rotate-image
957
+ spiral-matrix
958
+ set-matrix-zeroes
959
+ search-a-2d-matrix-ii`],
960
+ ['sliding-window', 'Sliding Window', `
961
+ longest-substring-without-repeating-characters
962
+ minimum-window-substring
963
+ sliding-window-maximum
964
+ find-all-anagrams-in-a-string`],
965
+ ['stack', 'Stack', `
966
+ valid-parentheses
967
+ largest-rectangle-in-histogram
968
+ min-stack
969
+ decode-string
970
+ daily-temperatures`],
971
+ ['two-pointers', 'Two Pointers', `
972
+ container-with-most-water
973
+ 3sum
974
+ trapping-rain-water
975
+ move-zeroes`],
976
+ ['trie', 'Trie', `
977
+ implement-trie-prefix-tree`],
978
+ ['misc', 'Misc', `
979
+ next-permutation
980
+ first-missing-positive
981
+ maximum-subarray
982
+ merge-intervals
983
+ sort-colors
984
+ single-number
985
+ majority-element
986
+ rotate-array
987
+ product-of-array-except-self
988
+ find-the-duplicate-number`],
989
+ ],
990
+ });
991
+
992
+ // LeetCode's own Top Interview 150 study plan, with its sections, as
993
+ // LeetCode's GraphQL (studyPlanV2Detail, plan top-interview-150) gave them on
994
+ // 2026-11-04: e2e/leetcode-study-plans.txt, which roadmap.test.mjs compares.
995
+ export const TOP_INTERVIEW_150 = defineList({
996
+ id: 'top-interview-150',
997
+ title: 'Top Interview 150 by LeetCode',
998
+ groupNoun: 'section',
999
+ groups: [
1000
+ ['array-string', 'Array / String', `
1001
+ merge-sorted-array
1002
+ remove-element
1003
+ remove-duplicates-from-sorted-array
1004
+ remove-duplicates-from-sorted-array-ii
1005
+ majority-element
1006
+ rotate-array
1007
+ best-time-to-buy-and-sell-stock
1008
+ best-time-to-buy-and-sell-stock-ii
1009
+ jump-game
1010
+ jump-game-ii
1011
+ h-index
1012
+ insert-delete-getrandom-o1
1013
+ product-of-array-except-self
1014
+ gas-station
1015
+ candy
1016
+ trapping-rain-water
1017
+ roman-to-integer
1018
+ integer-to-roman
1019
+ length-of-last-word
1020
+ longest-common-prefix
1021
+ reverse-words-in-a-string
1022
+ zigzag-conversion
1023
+ find-the-index-of-the-first-occurrence-in-a-string
1024
+ text-justification`],
1025
+ ['two-pointers', 'Two Pointers', `
1026
+ valid-palindrome
1027
+ is-subsequence
1028
+ two-sum-ii-input-array-is-sorted
1029
+ container-with-most-water
1030
+ 3sum`],
1031
+ ['sliding-window', 'Sliding Window', `
1032
+ minimum-size-subarray-sum
1033
+ longest-substring-without-repeating-characters
1034
+ substring-with-concatenation-of-all-words
1035
+ minimum-window-substring`],
1036
+ ['matrix', 'Matrix', `
1037
+ valid-sudoku
1038
+ spiral-matrix
1039
+ rotate-image
1040
+ set-matrix-zeroes
1041
+ game-of-life`],
1042
+ ['hashmap', 'Hashmap', `
1043
+ ransom-note
1044
+ isomorphic-strings
1045
+ word-pattern
1046
+ valid-anagram
1047
+ group-anagrams
1048
+ two-sum
1049
+ happy-number
1050
+ contains-duplicate-ii
1051
+ longest-consecutive-sequence`],
1052
+ ['intervals', 'Intervals', `
1053
+ summary-ranges
1054
+ merge-intervals
1055
+ insert-interval
1056
+ minimum-number-of-arrows-to-burst-balloons`],
1057
+ ['stack', 'Stack', `
1058
+ valid-parentheses
1059
+ simplify-path
1060
+ min-stack
1061
+ evaluate-reverse-polish-notation
1062
+ basic-calculator`],
1063
+ ['linked-list', 'Linked List', `
1064
+ linked-list-cycle
1065
+ add-two-numbers
1066
+ merge-two-sorted-lists
1067
+ copy-list-with-random-pointer
1068
+ reverse-linked-list-ii
1069
+ reverse-nodes-in-k-group
1070
+ remove-nth-node-from-end-of-list
1071
+ remove-duplicates-from-sorted-list-ii
1072
+ rotate-list
1073
+ partition-list
1074
+ lru-cache`],
1075
+ ['binary-tree-general', 'Binary Tree General', `
1076
+ maximum-depth-of-binary-tree
1077
+ same-tree
1078
+ invert-binary-tree
1079
+ symmetric-tree
1080
+ construct-binary-tree-from-preorder-and-inorder-traversal
1081
+ construct-binary-tree-from-inorder-and-postorder-traversal
1082
+ populating-next-right-pointers-in-each-node-ii
1083
+ flatten-binary-tree-to-linked-list
1084
+ path-sum
1085
+ sum-root-to-leaf-numbers
1086
+ binary-tree-maximum-path-sum
1087
+ binary-search-tree-iterator
1088
+ count-complete-tree-nodes
1089
+ lowest-common-ancestor-of-a-binary-tree`],
1090
+ ['binary-tree-bfs', 'Binary Tree BFS', `
1091
+ binary-tree-right-side-view
1092
+ average-of-levels-in-binary-tree
1093
+ binary-tree-level-order-traversal
1094
+ binary-tree-zigzag-level-order-traversal`],
1095
+ ['binary-search-tree', 'Binary Search Tree', `
1096
+ minimum-absolute-difference-in-bst
1097
+ kth-smallest-element-in-a-bst
1098
+ validate-binary-search-tree`],
1099
+ ['graph-general', 'Graph General', `
1100
+ number-of-islands
1101
+ surrounded-regions
1102
+ clone-graph
1103
+ evaluate-division
1104
+ course-schedule
1105
+ course-schedule-ii`],
1106
+ ['graph-bfs', 'Graph BFS', `
1107
+ snakes-and-ladders
1108
+ minimum-genetic-mutation
1109
+ word-ladder`],
1110
+ ['trie', 'Trie', `
1111
+ implement-trie-prefix-tree
1112
+ design-add-and-search-words-data-structure
1113
+ word-search-ii`],
1114
+ ['backtracking', 'Backtracking', `
1115
+ letter-combinations-of-a-phone-number
1116
+ combinations
1117
+ permutations
1118
+ combination-sum
1119
+ n-queens-ii
1120
+ generate-parentheses
1121
+ word-search`],
1122
+ ['divide-conquer', 'Divide & Conquer', `
1123
+ convert-sorted-array-to-binary-search-tree
1124
+ sort-list
1125
+ construct-quad-tree
1126
+ merge-k-sorted-lists`],
1127
+ ['kadane', "Kadane's Algorithm", `
1128
+ maximum-subarray
1129
+ maximum-sum-circular-subarray`],
1130
+ ['binary-search', 'Binary Search', `
1131
+ search-insert-position
1132
+ search-a-2d-matrix
1133
+ find-peak-element
1134
+ search-in-rotated-sorted-array
1135
+ find-first-and-last-position-of-element-in-sorted-array
1136
+ find-minimum-in-rotated-sorted-array
1137
+ median-of-two-sorted-arrays`],
1138
+ ['heap', 'Heap', `
1139
+ kth-largest-element-in-an-array
1140
+ ipo
1141
+ find-k-pairs-with-smallest-sums
1142
+ find-median-from-data-stream`],
1143
+ ['bit-manipulation', 'Bit Manipulation', `
1144
+ add-binary
1145
+ reverse-bits
1146
+ number-of-1-bits
1147
+ single-number
1148
+ single-number-ii
1149
+ bitwise-and-of-numbers-range`],
1150
+ ['math', 'Math', `
1151
+ palindrome-number
1152
+ plus-one
1153
+ factorial-trailing-zeroes
1154
+ sqrtx
1155
+ powx-n
1156
+ max-points-on-a-line`],
1157
+ ['dp-1d', '1D DP', `
1158
+ climbing-stairs
1159
+ house-robber
1160
+ word-break
1161
+ coin-change
1162
+ longest-increasing-subsequence`],
1163
+ ['dp-multi', 'Multidimensional DP', `
1164
+ triangle
1165
+ minimum-path-sum
1166
+ unique-paths-ii
1167
+ longest-palindromic-substring
1168
+ interleaving-string
1169
+ edit-distance
1170
+ best-time-to-buy-and-sell-stock-iii
1171
+ best-time-to-buy-and-sell-stock-iv
1172
+ maximal-square`],
1173
+ ],
1174
+ });
1175
+
1176
+ // LeetCode's own SQL 50 study plan (database problems, plan top-sql-50), with
1177
+ // its sections, checked against e2e/leetcode-study-plans.txt the same way.
1178
+ export const SQL_50 = defineList({
1179
+ id: 'sql-50',
1180
+ title: 'SQL 50',
1181
+ groupNoun: 'section',
1182
+ groups: [
1183
+ ['select', 'Select', `
1184
+ recyclable-and-low-fat-products
1185
+ find-customer-referee
1186
+ big-countries
1187
+ article-views-i
1188
+ invalid-tweets`],
1189
+ ['basic-joins', 'Basic Joins', `
1190
+ replace-employee-id-with-the-unique-identifier
1191
+ product-sales-analysis-i
1192
+ customer-who-visited-but-did-not-make-any-transactions
1193
+ rising-temperature
1194
+ average-time-of-process-per-machine
1195
+ employee-bonus
1196
+ students-and-examinations
1197
+ managers-with-at-least-5-direct-reports
1198
+ confirmation-rate`],
1199
+ ['basic-aggregate', 'Basic Aggregate Functions', `
1200
+ not-boring-movies
1201
+ average-selling-price
1202
+ project-employees-i
1203
+ percentage-of-users-attended-a-contest
1204
+ queries-quality-and-percentage
1205
+ monthly-transactions-i
1206
+ immediate-food-delivery-ii
1207
+ game-play-analysis-iv`],
1208
+ ['sorting-grouping', 'Sorting and Grouping', `
1209
+ number-of-unique-subjects-taught-by-each-teacher
1210
+ user-activity-for-the-past-30-days-i
1211
+ product-sales-analysis-iii
1212
+ classes-with-at-least-5-students
1213
+ find-followers-count
1214
+ biggest-single-number
1215
+ customers-who-bought-all-products`],
1216
+ ['advanced-select-joins', 'Advanced Select and Joins', `
1217
+ the-number-of-employees-which-report-to-each-employee
1218
+ primary-department-for-each-employee
1219
+ triangle-judgement
1220
+ consecutive-numbers
1221
+ product-price-at-a-given-date
1222
+ last-person-to-fit-in-the-bus
1223
+ count-salary-categories`],
1224
+ ['subqueries', 'Subqueries', `
1225
+ employees-whose-manager-left-the-company
1226
+ exchange-seats
1227
+ movie-rating
1228
+ restaurant-growth
1229
+ friend-requests-ii-who-has-the-most-friends
1230
+ investments-in-2016
1231
+ department-top-three-salaries`],
1232
+ ['advanced-string', 'Advanced String Functions / Regex / Clause', `
1233
+ fix-names-in-a-table
1234
+ patients-with-a-condition
1235
+ delete-duplicate-emails
1236
+ second-highest-salary
1237
+ group-sold-products-by-the-date
1238
+ list-the-products-ordered-in-a-period
1239
+ find-users-with-valid-e-mails`],
1240
+ ],
1241
+ });
1242
+
1243
+ // Every list, in the order the Progress tab offers them.
1244
+ export const LISTS = [NEETCODE_150, NEETCODE_250, BLIND_75, GRIND_75, TOP_100_LIKED, TOP_INTERVIEW_150, SQL_50];
1245
+ export const ROADMAPS = Object.fromEntries(LISTS.map((list) => [list.id, list]));
1246
+ export const DEFAULT_LIST_ID = NEETCODE_150.id;
1247
+
1248
+ // The list with this id; anything unknown (an old or damaged stored value)
1249
+ // means the default, NeetCode 150.
1250
+ export function listById(id) {
1251
+ return (typeof id === 'string' && Object.hasOwn(ROADMAPS, id) && ROADMAPS[id]) || NEETCODE_150;
1252
+ }
1253
+
1254
+ export function problemUrl(slug) {
1255
+ return `https://leetcode.com/problems/${slug}/`;
1256
+ }
1257
+
1258
+ function index(roadmap) {
1259
+ const bySlug = new Map();
1260
+ for (const topic of roadmap.topics) for (const p of topic.problems) bySlug.set(p.slug, p);
1261
+ return bySlug;
1262
+ }
1263
+
1264
+ // The problem as this list has it, or null when it is not on the list.
1265
+ export function problemInList(roadmap, slug) {
1266
+ return index(roadmap).get(slug) || null;
1267
+ }
1268
+
1269
+ // Solved and total counts per topic and overall. `solved` is any iterable of
1270
+ // slugs (the student's accepted submissions); slugs outside the roadmap are
1271
+ // ignored for the counts.
1272
+ export function progress(roadmap, solved) {
1273
+ const done = new Set(solved);
1274
+ const topics = roadmap.topics.map((topic) => {
1275
+ const solvedCount = topic.problems.filter((p) => done.has(p.slug)).length;
1276
+ return { id: topic.id, title: topic.title, solved: solvedCount, total: topic.problems.length, complete: solvedCount === topic.problems.length };
1277
+ });
1278
+ const solvedTotal = topics.reduce((n, t) => n + t.solved, 0);
1279
+ const total = topics.reduce((n, t) => n + t.total, 0);
1280
+ return { topics, solved: solvedTotal, total };
1281
+ }
1282
+
1283
+ // A topic is unlocked when every topic that points to it has at least one
1284
+ // problem solved (the roadmap is a guide, not a gate: nothing is blocked,
1285
+ // this only orders the suggestions). A list without edges has every group
1286
+ // unlocked, so its order alone decides.
1287
+ export function unlockedTopics(roadmap, solved) {
1288
+ const done = new Set(solved);
1289
+ const started = new Set(roadmap.topics.filter((t) => t.problems.some((p) => done.has(p.slug))).map((t) => t.id));
1290
+ return new Set(
1291
+ roadmap.topics
1292
+ .filter((t) => roadmap.edges.filter(([, to]) => to === t.id).every(([from]) => started.has(from)))
1293
+ .map((t) => t.id)
1294
+ );
1295
+ }
1296
+
1297
+ // What to do next, in the list's order: the first unsolved problems of the
1298
+ // earliest topic that is unlocked and not finished.
1299
+ export function nextOnRoadmap(roadmap, solved, { count = 1 } = {}) {
1300
+ const done = new Set(solved);
1301
+ const open = unlockedTopics(roadmap, done);
1302
+ const out = [];
1303
+ for (const topic of roadmap.topics) {
1304
+ if (!open.has(topic.id)) continue;
1305
+ for (const p of topic.problems) {
1306
+ if (!done.has(p.slug)) out.push(p);
1307
+ if (out.length >= count) return out;
1308
+ }
1309
+ }
1310
+ return out;
1311
+ }
1312
+
1313
+ // More of the same kind: unsolved problems in the topic of the one just
1314
+ // solved (its subject, on a list that has subjects), easiest first.
1315
+ export function moreOfTopic(roadmap, solved, slug, { count = 1 } = {}) {
1316
+ const done = new Set(solved);
1317
+ const problem = index(roadmap).get(slug);
1318
+ if (!problem) return { topic: null, problems: [] };
1319
+ const topic = roadmap.subjects ? roadmap.subjects.find((t) => t.id === problem.subject) : roadmap.topics.find((t) => t.id === problem.topic);
1320
+ const rank = { Easy: 0, Medium: 1, Hard: 2 };
1321
+ const problems = topic.problems
1322
+ .filter((p) => !done.has(p.slug) && p.slug !== slug)
1323
+ .sort((a, b) => rank[a.difficulty] - rank[b.difficulty])
1324
+ .slice(0, count);
1325
+ return { topic: { id: topic.id, title: topic.title }, problems };
1326
+ }
1327
+
1328
+ // Everything the "nice job" message needs after an accepted submission.
1329
+ export function suggestAfterSolve(roadmap, solved, slug, { count = 1 } = {}) {
1330
+ const done = new Set(solved);
1331
+ done.add(slug);
1332
+ return {
1333
+ roadmap: { id: roadmap.id, title: roadmap.title },
1334
+ agenda: nextOnRoadmap(roadmap, done, { count }),
1335
+ sameTopic: moreOfTopic(roadmap, done, slug, { count }),
1336
+ };
1337
+ }