Skip to content

v1.2.0 — 9 new utilities, tests, bug fixes#8

Merged
Amitind merged 4 commits intomainfrom
feat/v1.2.0-improvements
Mar 29, 2026
Merged

v1.2.0 — 9 new utilities, tests, bug fixes#8
Amitind merged 4 commits intomainfrom
feat/v1.2.0-improvements

Conversation

@Amitind
Copy link
Copy Markdown
Owner

@Amitind Amitind commented Mar 29, 2026

Changes

New Functions (9)

  • slugify(text) — URL-safe slug generation
  • truncate(str, length) — String truncation with ellipsis
  • sleep(ms) — Async delay helper
  • chunk(array, size) — Array chunking
  • formatNumber(num) — Number formatting (1.5K, 2M, 3.5B)
  • debounce(fn, delay) — Function debouncing
  • capitalize(str) — First letter capitalization
  • isEmpty(value) — Empty value detection
  • deepClone(obj) — Deep object cloning

Fixes

  • shuffleArray() no longer mutates the original array
  • Renamed uniqueArrayitemsuniqueArrayItems (old name kept as deprecated alias)
  • Added security warning to randomString()

Added

  • Vitest test suite — 10 tests, all passing
  • CHANGELOG.md for version tracking
  • Version bumped to 1.2.0

New functions:
- slugify, truncate, sleep, chunk, formatNumber
- debounce, capitalize, isEmpty, deepClone

Fixes:
- shuffleArray no longer mutates original
- uniqueArrayitems → uniqueArrayItems (typo)
- randomString security warning added

Added:
- Vitest test suite
- CHANGELOG.md
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 29, 2026

Summary by CodeRabbit

  • New Features

    • Added nine new utilities for string handling, array operations, number formatting, debouncing, timing, chunking, and deep cloning; release bumped to v1.2.0.
  • Bug Fixes

    • Shuffle operation no longer mutates the original array.
    • Renamed a utility for consistency and kept a deprecated alias for compatibility.
    • Clarified that the random-string utility is not cryptographically secure.
  • Documentation

    • Added a changelog.
  • Tests

    • Added a comprehensive test suite covering all utilities.

Walkthrough

Bumps package to v1.2.0; adds utilities (slugify, truncate, sleep, chunk, formatNumber, debounce, capitalize, isEmpty, deepClone); makes shuffleArray non‑mutating; renames uniqueArrayitems → uniqueArrayItems with deprecated alias; updates randomString JSDoc; adds Vitest tests and CHANGELOG.md.

Changes

Cohort / File(s) Summary
Configuration & Versioning
jsr.json, package.json
Package version updated 1.1.2 → 1.2.0; package.json adds vitest devDependency, replaces test script with vitest run, and adds test:watch.
Release Notes
CHANGELOG.md
New changelog documenting v1.2.0: added utilities, fixes (non‑mutating shuffle, rename), security note for randomString, and test suite mention.
Core Utilities
src/main.ts
Added exports: slugify, truncate, sleep, chunk (throws if size < 1), formatNumber, debounce (with cancel/flush), capitalize, isEmpty, deepClone; shuffleArray now returns a shuffled copy (non‑mutating); renamed uniqueArrayitemsuniqueArrayItems and retained deprecated alias uniqueArrayitems; updated randomString JSDoc warning.
Tests
src/__tests__/main.test.ts
New Vitest test suite covering many utilities: randomString defaults, non‑mutation of shuffleArray, slugify, truncate, chunk edge cases, formatNumber, capitalize, isEmpty, sleep, debounce behavior (including cancel), and deepClone.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main changes: version 1.2.0 with 9 new utilities, tests, and bug fixes, matching the changeset content.
Description check ✅ Passed The description is directly related to the changeset, providing detailed categorized information about the 9 new functions, fixes applied, and additions made.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/v1.2.0-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread src/main.ts
Comment on lines +61 to +66
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '');

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '-'.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d65e7a366

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/__tests__/main.test.ts Outdated
Comment on lines +3 to +5
randomString, randomNumber, binary, shuffleArray,
randomArrayItem, randomItemsArray, uniqueArrayItems,
slugify, truncate, chunk, formatNumber, debounce,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove unused test imports to restore TypeScript build

With noUnusedLocals enabled in tsconfig.json, tsc fails on this test file because several imported helpers are never referenced (randomNumber, binary, randomArrayItem, randomItemsArray, uniqueArrayItems, debounce). Since npm run build runs tsc first, this blocks normal builds/publishing for the package until the unused imports are removed or used.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Line 7: The markdown headings like "### Added" (and the other H3 headings
flagged) need a blank line immediately after them to satisfy the linter; update
the CHANGELOG.md by inserting a single empty line after each "### ..." heading
(e.g., after "### Added") so there is a blank line between the heading and the
following content.

In `@src/__tests__/main.test.ts`:
- Around line 2-7: Add missing unit tests for the imported utilities: import and
test randomNumber (assert value within min/max and integer/float behavior),
binary (returns only 0 or 1 over many samples), randomArrayItem (returns an
element from a given array), randomItemsArray (returns specified count of items
from array and respects bounds), uniqueArrayItems (removes duplicates and
preserves expected length/content), and sleep (import sleep and await it
resolves after the given ms). For debounce, add a timing-based test using Jest
fake timers: wrap a mock function with debounce, call it multiple times rapidly,
advance timers and assert the wrapped function is invoked only once after the
debounce delay. Use the function names randomNumber, binary, randomArrayItem,
randomItemsArray, uniqueArrayItems, debounce, and sleep to locate code.

In `@src/main.ts`:
- Around line 120-123: The deepClone function uses
JSON.parse(JSON.stringify(obj)) which silently loses undefined and function
properties, converts Date objects to ISO strings, cannot clone
Map/Set/RegExp/TypedArray, and will throw on circular references; update the
JSDoc for deepClone to list these limitations explicitly (mention undefined,
functions, Dates -> strings, Map/Set/RegExp/TypedArray not supported, and
circular reference errors) and add a short recommendation (e.g., use a
specialized cloning utility or structuredClone when available) so callers know
the caveats for function deepClone.
- Around line 27-31: The current shuffleArray function uses [...array].sort(()
=> Math.random() - 0.5) which yields a biased distribution; replace it with an
unbiased Fisher-Yates implementation: make a shallow copy of the input (to avoid
mutating original), then perform an in-place Fisher-Yates shuffle by iterating
from the end to the start, swapping each element with a random earlier index
(use Math.floor(Math.random() * (i + 1))). Update the function
shuffleArray<T>(array: T[]): T[] to return the shuffled copy.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 67d6e753-8006-4820-a194-94b6f11574b1

📥 Commits

Reviewing files that changed from the base of the PR and between 437ecc4 and 8d65e7a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • CHANGELOG.md
  • jsr.json
  • package.json
  • src/__tests__/main.test.ts
  • src/main.ts
📜 Review details
🧰 Additional context used
🪛 GitHub Check: CodeQL
src/main.ts

[failure] 61-66: Polynomial regular expression used on uncontrolled data
This regular expression that depends on library input may run slow on strings with many repetitions of '-'.

🪛 markdownlint-cli2 (0.22.0)
CHANGELOG.md

[warning] 7-7: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 19-19: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 26-26: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🔇 Additional comments (21)
package.json (1)

3-3: LGTM!

Version bump to 1.2.0 aligns with the changelog, and the Vitest integration is properly configured with both run and watch modes.

Also applies to: 23-24, 48-49

jsr.json (1)

6-6: LGTM!

Version bump to 1.2.0 is consistent with package.json.

src/__tests__/main.test.ts (9)

9-13: LGTM!

The randomString test validates the output length correctly.


15-22: LGTM!

Good test for verifying the non-mutation fix to shuffleArray.


24-29: LGTM!

Covers both basic slugification and edge cases with spaces and special characters.


31-38: LGTM!

Tests both truncation and the pass-through case for short strings.


40-44: LGTM!

Tests chunking including the remainder case.


46-53: LGTM!

Good coverage of K/M/B suffixes and the pass-through case for small numbers.


55-59: LGTM!

Basic capitalization test is adequate.


61-71: LGTM!

Comprehensive coverage of empty value detection for various types.


73-80: LGTM!

Correctly verifies that the clone is independent from the original.

src/main.ts (10)

5-5: LGTM!

Good addition of a security warning clarifying that randomString is not suitable for cryptographic purposes.


52-57: LGTM!

Good approach maintaining backward compatibility with the deprecated alias while fixing the casing inconsistency.


70-73: LGTM!

Clean implementation with proper boundary check.


75-78: LGTM!

Standard async sleep implementation.


81-88: LGTM!

Good validation for chunk size with clear error message.


91-96: LGTM!

Clean number formatting with appropriate suffixes and decimal handling.


99-105: LGTM!

Standard debounce implementation. Note that it doesn't return the function's result, which is typical for debounce utilities focused on side effects.


108-110: LGTM!

Simple and correct capitalization.


113-118: LGTM!

Handles the common empty value cases appropriately.


59-67: The ReDoS vulnerability claim is unsubstantiated.

The regex [\s_-]+ is a character class with a single quantifier. Character classes are atomic—they don't backtrack internally. ReDoS vulnerabilities require nested quantifiers (e.g., (a+)+) or overlapping alternation (e.g., (a|a)+), neither of which exist here. The pattern is used in a sequential .replace() context without anchoring that would force re-matching from multiple positions, making it linear time O(n). While CodeQL may conservatively flag patterns as precautionary, this specific pattern is not vulnerable to polynomial backtracking in this context.

Comment thread CHANGELOG.md
Comment thread src/__tests__/main.test.ts Outdated
Comment thread src/main.ts Outdated
Comment thread src/main.ts Outdated
- Remove unused imports from test file (fixes tsc build)
- Use Fisher-Yates shuffle for uniform distribution
- Add blank lines after CHANGELOG headings
- Document deepClone limitations in JSDoc
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/__tests__/main.test.ts`:
- Around line 27-31: The test mutates arrays by calling sort() directly; update
the assertion to avoid in-place mutation by sorting copies instead (e.g., sort
cloned arrays) so neither original nor shuffled is mutated; locate the test
around the shuffleArray invocation in the spec (function name shuffleArray) and
replace expect(shuffled.sort()).toEqual(original.sort()) with an assertion that
compares sorted clones (or uses array-contains matchers) to ensure immutability
during the assertion.

In `@src/main.ts`:
- Around line 103-110: The debounce function should expose cancellation (and
optionally flush) capabilities; modify export function debounce<T>(fn: T, delay
= 300) to return a callable that also has methods cancel() to clear the pending
timeout and prevent the delayed call, and flush() to immediately invoke the
pending call if any and clear the timeout. Ensure the returned value is typed to
include these methods (e.g., a function with attached cancel/flush properties),
use the existing timeoutId variable to implement both cancel and flush, and
preserve the current behavior of the main debounced function.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: be1104a1-a7d4-46e2-b88f-f61d14bebd47

📥 Commits

Reviewing files that changed from the base of the PR and between 8d65e7a and 500149b.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • src/__tests__/main.test.ts
  • src/main.ts
📜 Review details
🧰 Additional context used
🪛 GitHub Check: CodeQL
src/main.ts

[failure] 66-71: Polynomial regular expression used on uncontrolled data
This regular expression that depends on library input may run slow on strings with many repetitions of '-'.

🔇 Additional comments (7)
CHANGELOG.md (1)

1-32: LGTM!

The CHANGELOG follows the Keep a Changelog format well, documenting the new utilities, fixes, and version history appropriately. The blank line issue from the previous review has been addressed.

src/__tests__/main.test.ts (1)

1-12: Good test structure and coverage for the new utilities.

The test suite covers the key behaviors of the newly added functions, including edge cases like invalid chunk sizes. The imports align with the tested functions.

src/main.ts (5)

27-37: LGTM!

The Fisher–Yates shuffle is correctly implemented and the function now properly avoids mutating the original array. This addresses the previous review feedback.


125-128: LGTM!

The JSDoc now properly documents the JSON-based cloning limitations (functions, undefined, Dates, circular refs). This addresses the previous review feedback.


57-62: Good deprecation pattern.

The alias preserves backward compatibility while encouraging migration to the correctly-cased name.


85-93: LGTM!

The chunk function correctly validates input and handles the edge case of arrays not evenly divisible by the chunk size.


64-72: No ReDoS concern with the slugify regex.

The regex /[\s_-]+/g at line 70 is safe from ReDoS—character classes with simple quantifiers do not exhibit catastrophic backtracking behavior. No input validation or documentation is needed for this reason.

			> Likely an incorrect or invalid review comment.

Comment thread src/__tests__/main.test.ts
Comment thread src/main.ts
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 29, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 2 file(s) based on 2 unresolved review comments.

Files modified:

  • src/__tests__/main.test.ts
  • src/main.ts

Commit: 42bd7a3ac4c32d8ea5ce59420a78f49ce3721327

The changes have been pushed to the feat/v1.2.0-improvements branch.

Time taken: 3m 50s

Fixed 2 file(s) based on 2 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main.ts`:
- Around line 135-141: The isEmpty function currently treats Maps, Sets and
functions as empty because Object.keys() returns [] for them; update isEmpty to
explicitly handle these types: add checks in isEmpty for value instanceof Map
and value instanceof Set (use their .size to determine emptiness) and for typeof
value === 'function' (decide and document whether a function should be
considered empty—typically treat as not empty or return false), and adjust the
object branch to only treat plain objects (e.g., value !== null &&
value.constructor === Object) via Object.keys; also add a JSDoc comment on
isEmpty describing behavior for Map, Set, and functions so callers know the
edge-case decisions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5a8eee96-9ab3-4a04-9e6b-e212dc97ef22

📥 Commits

Reviewing files that changed from the base of the PR and between 500149b and 42bd7a3.

📒 Files selected for processing (2)
  • src/__tests__/main.test.ts
  • src/main.ts
📜 Review details
🧰 Additional context used
🪛 GitHub Check: CodeQL
src/main.ts

[failure] 66-71: Polynomial regular expression used on uncontrolled data
This regular expression that depends on library input may run slow on strings with many repetitions of '-'.

🔇 Additional comments (13)
src/__tests__/main.test.ts (2)

1-12: Missing test imports and coverage for several utilities.

The test file imports 9 functions but the module exports additional utilities that lack test coverage: randomNumber, binary, randomArrayItem, randomItemsArray, uniqueArrayItems, debounce, and sleep. These are imported but not tested, which was flagged previously.


14-93: LGTM!

The test suite provides good coverage for the new utilities. The tests are well-structured with clear assertions:

  • shuffleArray correctly tests non-mutation using spread operators
  • chunk tests both success and error cases
  • isEmpty covers multiple edge cases (null, undefined, empty string, array, object)
  • deepClone properly verifies isolation between original and clone
src/main.ts (11)

3-11: LGTM!

Good addition of the security warning. The JSDoc clearly communicates that randomString should not be used for cryptographic purposes and suggests crypto.randomUUID() as an alternative.


27-37: LGTM!

The Fisher-Yates implementation is correct and provides uniform shuffle distribution. The function properly creates a copy to avoid mutating the original array.


57-62: LGTM!

Good approach to the rename: the deprecated alias uniqueArrayitems maintains backward compatibility while the corrected uniqueArrayItems becomes the canonical export.


74-78: LGTM!

Clean implementation. The trimEnd() before adding ellipsis is a nice touch to avoid awkward trailing spaces.


80-83: LGTM!

Standard async sleep implementation using Promise and setTimeout.


85-93: LGTM!

Correct chunking implementation with proper validation for the size parameter.


95-101: LGTM!

Clean implementation with appropriate suffixes. The .replace(/\.0$/, '') nicely handles whole number formatting (e.g., 2M instead of 2.0M).


103-128: LGTM!

Well-implemented debounce with cancel() and flush() methods. The lastArgs tracking enables proper flush behavior.


130-133: LGTM!

Simple and correct implementation. Handles empty strings gracefully.


143-146: LGTM!

The JSDoc now clearly documents the JSON-based cloning limitations (functions, undefined, Dates, circular references). Users can make informed decisions about when to use this utility.


64-72: The ReDoS concern is a false positive; no changes are required.

The regex pattern [\s_-]+ is not vulnerable to catastrophic backtracking. ReDoS attacks require nested quantifiers or overlapping alternations to trigger exponential backtracking, but this pattern uses a simple character class with a single quantifier, which matches in O(n) linear time. The CodeQL warning can be safely dismissed.

Comment thread src/main.ts Outdated
Comment on lines +135 to +141
/** Check if value is empty (null, undefined, empty string, empty array, empty object) */
export function isEmpty(value: unknown): boolean {
if (value == null) return true;
if (typeof value === 'string' || Array.isArray(value)) return value.length === 0;
if (typeof value === 'object') return Object.keys(value).length === 0;
return false;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider documenting edge cases for Map, Set, and functions.

The current implementation returns true for new Map(), new Set(), and empty functions because Object.keys() returns an empty array for these. This may be unexpected behavior.

📝 Optional: Add JSDoc clarification
-/** Check if value is empty (null, undefined, empty string, empty array, empty object) */
+/** Check if value is empty (null, undefined, empty string, empty array, empty plain object)
+ * Note: Returns true for Map/Set/functions as they have no enumerable keys */
 export function isEmpty(value: unknown): boolean {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.ts` around lines 135 - 141, The isEmpty function currently treats
Maps, Sets and functions as empty because Object.keys() returns [] for them;
update isEmpty to explicitly handle these types: add checks in isEmpty for value
instanceof Map and value instanceof Set (use their .size to determine emptiness)
and for typeof value === 'function' (decide and document whether a function
should be considered empty—typically treat as not empty or return false), and
adjust the object branch to only treat plain objects (e.g., value !== null &&
value.constructor === Object) via Object.keys; also add a JSDoc comment on
isEmpty describing behavior for Map, Set, and functions so callers know the
edge-case decisions.

- Add tests for all 16 functions (25 tests total)
- isEmpty now handles Map and Set
- debounce cancel and flush already existed, added tests
- Remove unused vi import
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/main.ts (1)

64-72: ⚠️ Potential issue | 🟡 Minor

Address CodeQL warning: potential ReDoS on uncontrolled input.

Static analysis flags the regex chain as potentially slow with pathological inputs (many repetitions of -). While the risk is mitigated because line 70 collapses consecutive hyphens before line 71 processes them, this function accepts uncontrolled library input.

Consider adding an input length guard or simplifying the regex chain to eliminate the concern entirely.

🛡️ Option 1: Add length guard
 /** Slugify a string for URLs */
 export function slugify(text: string): string {
+	if (text.length > 1000) {
+		text = text.slice(0, 1000);
+	}
 	return text
 		.toLowerCase()
 		.trim()
🛡️ Option 2: Combine regex operations
 export function slugify(text: string): string {
 	return text
 		.toLowerCase()
 		.trim()
-		.replace(/[^\w\s-]/g, '')
-		.replace(/[\s_-]+/g, '-')
-		.replace(/^-+|-+$/g, '');
+		.replace(/[^\w\s-]/g, '')
+		.replace(/[\s_-]+/g, '-')
+		.replace(/^-|-$/g, '');
 }

Note: Replacing -+ with - works because consecutive hyphens are already collapsed by the previous step.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.ts` around lines 64 - 72, The slugify function is flagged for
potential ReDoS due to chained regexes on uncontrolled input; add a defensive
input-length guard at the start of slugify (e.g., if text.length > 2000 then
truncate to a safe max or return early) and then run the existing transforms, or
alternatively simplify the regex chain by combining the consecutive collapse
steps into a single safe replace (use one .replace(/[\s_-]+/g, '-') before
trimming with .replace(/^-+|-+$/g, '')). Update function slugify to include this
length check or the combined regex to eliminate the ReDoS risk.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/__tests__/main.test.ts`:
- Around line 136-155: Add a test that verifies debounce(...).flush()
immediately invokes a pending call: create a local counter, call const fn =
debounce(() => { count++; }, 50); call fn() to schedule it, assert count is
still 0, call fn.flush(), then assert count is 1 (and optionally await
sleep(100) to ensure no double-invocation); also include a test that calling
flush after cancel does not run the function by using fn.cancel() followed by
fn.flush() and asserting count remains 0. Ensure you reference the debounce
function and its methods flush() and cancel() and reuse sleep() helper and the
count variable used in existing tests.

In `@src/main.ts`:
- Around line 95-101: The formatNumber function treats negatives as plain
numbers because its thresholds use num >= ..., so update formatNumber to compare
using the absolute value (e.g., const abs = Math.abs(num)) and determine the
sign (e.g., const sign = num < 0 ? '-' : ''), then format abs with the K/M/B
logic and return sign + formattedAbs (preserving the original zero/decimal
trimming behavior); locate the logic in the formatNumber function to implement
this change.

---

Duplicate comments:
In `@src/main.ts`:
- Around line 64-72: The slugify function is flagged for potential ReDoS due to
chained regexes on uncontrolled input; add a defensive input-length guard at the
start of slugify (e.g., if text.length > 2000 then truncate to a safe max or
return early) and then run the existing transforms, or alternatively simplify
the regex chain by combining the consecutive collapse steps into a single safe
replace (use one .replace(/[\s_-]+/g, '-') before trimming with
.replace(/^-+|-+$/g, '')). Update function slugify to include this length check
or the combined regex to eliminate the ReDoS risk.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a47333ec-c788-4bdf-81b4-b2b5a82924c6

📥 Commits

Reviewing files that changed from the base of the PR and between 42bd7a3 and 971578a.

📒 Files selected for processing (2)
  • src/__tests__/main.test.ts
  • src/main.ts
📜 Review details
🧰 Additional context used
🪛 GitHub Check: CodeQL
src/main.ts

[failure] 66-71: Polynomial regular expression used on uncontrolled data
This regular expression that depends on library input may run slow on strings with many repetitions of '-'.

🔇 Additional comments (11)
src/__tests__/main.test.ts (3)

1-19: LGTM!

Imports are clean and comprehensive, covering all 16 exported utility functions from main.ts.


50-62: LGTM!

The mutation concern from the previous review has been properly addressed using spread operators in the assertion.


163-179: LGTM!

Comprehensive isEmpty tests covering primitives, collections, and the newly added Map/Set support.

src/main.ts (8)

5-5: LGTM!

Good addition of the security warning in the JSDoc. This helps users understand when to use crypto.randomUUID() instead.


27-37: LGTM!

Correctly implements Fisher-Yates shuffle, ensuring uniform distribution and non-mutating behavior as documented.


74-78: LGTM!

Clean implementation with trimEnd() ensuring no trailing whitespace before the ellipsis.


85-93: LGTM!

Good input validation with a descriptive error message. The loop-based implementation is clear and efficient.


103-128: LGTM!

Well-implemented debounce with cancel() and flush() methods. The lastArgs tracking ensures flush() can invoke the function with the most recent arguments.


135-142: LGTM!

Comprehensive implementation covering all documented types including Map and Set. The JSDoc accurately reflects the supported types.


144-147: LGTM!

The JSDoc clearly documents the JSON-based limitations. This is appropriate for a simple utility library.


57-62: LGTM!

Good backward compatibility approach with a clear @deprecated JSDoc tag for the old name.

Comment on lines +136 to +155
describe('debounce', () => {
it('delays function execution', async () => {
let count = 0;
const fn = debounce(() => { count++; }, 50);
fn();
fn();
fn();
expect(count).toBe(0);
await sleep(100);
expect(count).toBe(1);
});
it('cancel prevents execution', async () => {
let count = 0;
const fn = debounce(() => { count++; }, 50);
fn();
fn.cancel();
await sleep(100);
expect(count).toBe(0);
});
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Missing test coverage for debounce.flush() method.

The debounce implementation in main.ts exposes both cancel() and flush() methods, but only cancel() is tested here. Consider adding a test case for flush() to ensure it immediately invokes the pending function.

🧪 Proposed test for flush()
 	it('cancel prevents execution', async () => {
 		let count = 0;
 		const fn = debounce(() => { count++; }, 50);
 		fn();
 		fn.cancel();
 		await sleep(100);
 		expect(count).toBe(0);
 	});
+	it('flush immediately invokes pending call', () => {
+		let count = 0;
+		const fn = debounce(() => { count++; }, 50);
+		fn();
+		expect(count).toBe(0);
+		fn.flush();
+		expect(count).toBe(1);
+	});
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
describe('debounce', () => {
it('delays function execution', async () => {
let count = 0;
const fn = debounce(() => { count++; }, 50);
fn();
fn();
fn();
expect(count).toBe(0);
await sleep(100);
expect(count).toBe(1);
});
it('cancel prevents execution', async () => {
let count = 0;
const fn = debounce(() => { count++; }, 50);
fn();
fn.cancel();
await sleep(100);
expect(count).toBe(0);
});
});
describe('debounce', () => {
it('delays function execution', async () => {
let count = 0;
const fn = debounce(() => { count++; }, 50);
fn();
fn();
fn();
expect(count).toBe(0);
await sleep(100);
expect(count).toBe(1);
});
it('cancel prevents execution', async () => {
let count = 0;
const fn = debounce(() => { count++; }, 50);
fn();
fn.cancel();
await sleep(100);
expect(count).toBe(0);
});
it('flush immediately invokes pending call', () => {
let count = 0;
const fn = debounce(() => { count++; }, 50);
fn();
expect(count).toBe(0);
fn.flush();
expect(count).toBe(1);
});
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/main.test.ts` around lines 136 - 155, Add a test that verifies
debounce(...).flush() immediately invokes a pending call: create a local
counter, call const fn = debounce(() => { count++; }, 50); call fn() to schedule
it, assert count is still 0, call fn.flush(), then assert count is 1 (and
optionally await sleep(100) to ensure no double-invocation); also include a test
that calling flush after cancel does not run the function by using fn.cancel()
followed by fn.flush() and asserting count remains 0. Ensure you reference the
debounce function and its methods flush() and cancel() and reuse sleep() helper
and the count variable used in existing tests.

Comment thread src/main.ts
Comment on lines +95 to +101
/** Format number with K/M/B suffixes */
export function formatNumber(num: number): string {
if (num >= 1_000_000_000) return (num / 1_000_000_000).toFixed(1).replace(/\.0$/, '') + 'B';
if (num >= 1_000_000) return (num / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M';
if (num >= 1_000) return (num / 1_000).toFixed(1).replace(/\.0$/, '') + 'K';
return num.toString();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Negative numbers don't receive K/M/B suffixes.

The conditions check num >= 1_000 etc., so negative numbers like -1500 return "-1500" instead of "-1.5K". If this is intentional, consider documenting the behavior; otherwise, use Math.abs(num) for comparisons.

♻️ Optional fix to handle negative numbers
 /** Format number with K/M/B suffixes */
 export function formatNumber(num: number): string {
-	if (num >= 1_000_000_000) return (num / 1_000_000_000).toFixed(1).replace(/\.0$/, '') + 'B';
-	if (num >= 1_000_000) return (num / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M';
-	if (num >= 1_000) return (num / 1_000).toFixed(1).replace(/\.0$/, '') + 'K';
+	const abs = Math.abs(num);
+	if (abs >= 1_000_000_000) return (num / 1_000_000_000).toFixed(1).replace(/\.0$/, '') + 'B';
+	if (abs >= 1_000_000) return (num / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M';
+	if (abs >= 1_000) return (num / 1_000).toFixed(1).replace(/\.0$/, '') + 'K';
 	return num.toString();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/** Format number with K/M/B suffixes */
export function formatNumber(num: number): string {
if (num >= 1_000_000_000) return (num / 1_000_000_000).toFixed(1).replace(/\.0$/, '') + 'B';
if (num >= 1_000_000) return (num / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M';
if (num >= 1_000) return (num / 1_000).toFixed(1).replace(/\.0$/, '') + 'K';
return num.toString();
}
/** Format number with K/M/B suffixes */
export function formatNumber(num: number): string {
const abs = Math.abs(num);
if (abs >= 1_000_000_000) return (num / 1_000_000_000).toFixed(1).replace(/\.0$/, '') + 'B';
if (abs >= 1_000_000) return (num / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M';
if (abs >= 1_000) return (num / 1_000).toFixed(1).replace(/\.0$/, '') + 'K';
return num.toString();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.ts` around lines 95 - 101, The formatNumber function treats
negatives as plain numbers because its thresholds use num >= ..., so update
formatNumber to compare using the absolute value (e.g., const abs =
Math.abs(num)) and determine the sign (e.g., const sign = num < 0 ? '-' : ''),
then format abs with the K/M/B logic and return sign + formattedAbs (preserving
the original zero/decimal trimming behavior); locate the logic in the
formatNumber function to implement this change.

@Amitind Amitind merged commit a7f4922 into main Mar 29, 2026
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants