Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/rss-reader/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ function decodeEntities(value: string): string {

return value.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match, entity: string) => {
if (entity.startsWith('#x')) {
const codePoint = Number.parseInt(entity.slice(2), 16);
const codePoint = Number.parseInt(entity.slice(2, 10), 16);
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;
}

if (entity.startsWith('#')) {
const codePoint = Number.parseInt(entity.slice(1), 10);
const codePoint = Number.parseInt(entity.slice(1, 10), 10);
Comment on lines +30 to +35

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

node -e 'for (const value of [0xFFFFFFFF, 99999999]) { try { String.fromCodePoint(value); process.exit(1); } catch {} }'

Repository: Karanjot786/TermUI

Length of output: 156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files matching index.tsx:\n'
fd -a 'index\.tsx$' . | sed 's#^\./##'

printf '\nContext around examples/rss-reader/src/index.tsx lines 1-80:\n'
if [ -f examples/rss-reader/src/index.tsx ]; then
  nl -ba examples/rss-reader/src/index.tsx | sed -n '1,80p'
else
  echo 'examples/rss-reader/src/index.tsx not found'
fi

printf '\nSearch for entity decoding functions/usages:\n'
rg -n "fromCodePoint|parseEntity|entity|Number\.parseInt|slice\(1, 10\)|slice\(2, 10\)" examples/rss-reader/src/index.tsx .

Repository: Karanjot786/TermUI

Length of output: 2185


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'File size:\n'
wc -l examples/rss-reader/src/index.tsx 2>/dev/null || true

printf '\nContext around examples/rss-reader/src/index.tsx lines 1-80:\n'
sed -n '1,80p' examples/rss-reader/src/index.tsx

printf '\nSearch for entity decoding terms in repository:\n'
rg -n "fromCodePoint|Number\.parseInt|slice\(1, 10\)|slice\(2, 10\)|&#" examples/rss-reader/src/index.tsx || true

printf '\nBehavioral probes for String.fromCodePoint:\n'
node - <<'JS'
const inputs = [0xFFFFFFFF, 99999999, 0x10FFFF, 0x110000, 0];
for (const value of inputs) {
  try {
    const s = String.fromCodePoint(value);
    console.log(`${value} => finite=${Number.isFinite(value)} => "${s}" (code=${s.codePointAt(0)?.toString(16).toUpperCase()})`);
  } catch (e) {
    console.log(`${value} => finite=${Number.isFinite(value)} => throws ${e.name}: ${e.message}`);
  }
}
JS

Repository: Karanjot786/TermUI

Length of output: 3268


Validate the Unicode range and entity length before replacing the entity.

Number.isFinite does not validate a Unicode code point. Inputs such as &#xFFFFFFFF; or &#99999999; can pass this check and make String.fromCodePoint throw RangeError. Reject values outside 0..0x10FFFF and reject entities longer than eight digits so the decoder does not decode a truncated prefix as a different code point.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/rss-reader/src/index.tsx` around lines 30 - 35, Update the
entity-decoding logic around the codePoint parsing and replacement branches to
validate the full numeric entity before calling String.fromCodePoint: require
1–8 digits, ensure the value is finite and within 0..0x10FFFF, and return the
original match for invalid or oversized entities. Apply the same validation to
both hexadecimal and decimal entities, preventing truncated prefixes from being
decoded.

return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : match;
}

Expand Down
2 changes: 1 addition & 1 deletion examples/showcase/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class ShowcaseApp extends Widget {
if (event.key === 'q' || (event.ctrl && event.key === 'c')) return false;

// Tab switching: 1-5
const num = parseInt(event.key);
const num = parseInt(event.key, 10);
if (num >= 1 && num <= 5) {
this.switchTab(num - 1);
return true;
Expand Down
2 changes: 1 addition & 1 deletion examples/widget-gallery/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class WidgetGalleryApp extends Widget {
}

// Tab switching: 1-6
const num = parseInt(event.key);
const num = parseInt(event.key, 10);
if (num >= 1 && num <= 6) {
this._switchTab(num - 1);
return true;
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/TreeSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ function _pathsEqual(a: number[], b: number[]): boolean {

function _valuesEqual(a: string[], b: string[]): boolean {
if (a.length !== b.length) return false;
const sortedA = [...a].sort();
const sortedB = [...b].sort();
const sortedA = [...a].sort((a, b) => a - b);
const sortedB = [...b].sort((a, b) => a - b);
Comment on lines +185 to +186

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files 'packages/ui/src/TreeSelect.ts' 'scripts/build-registry.ts' 'tsconfig*.json' 2>/dev/null || true

echo
echo "== package type settings =="
for f in $(git ls-files 'tsconfig*.json' 'packages/**/tsconfig*.json' 'scripts/**/tsconfig*.json' 2>/dev/null | head -50); do
  echo "--- $f"
  sed -n '1,220p' "$f" | rg -n '"strict"|"moduleResolution"|"noImplicitAny"|"target"|"lib"|compilerOptions' || true
done

echo
echo "== TreeSelect relevant lines =="
sed -n '1,240p' packages/ui/src/TreeSelect.ts 2>/dev/null || true

echo
echo "== build-registry relevant lines =="
sed -n '1,120p' scripts/build-registry.ts 2>/dev/null || true

echo
echo "== occurrences =="
rg -n "sort\\(\\(a, b\\) => a - b\\)|sort\\(\\(a, b\\) =>" -S packages/ui/src scripts/build-registry.ts packages 2>/dev/null || true

echo
echo "== TypeScript availability/parser probe =="
node -e "const fs=require('node:fs'); for (const p of ['packages/ui/src/TreeSelect.ts','scripts/build-registry.ts']) console.log(p, 'exists', fs.existsSync(p));"
if command -v npx >/dev/null 2>&1; then
  echo "npx available"
  npx tsc --version 2>/dev/null || true
else
  echo "npx unavailable"
fi

python3 - <<'PY'
import subprocess, textwrap, json, re, os
files = ['packages/ui/src/TreeSelect.ts','scripts/build-registry.ts']
for f in files:
    print(f"--- {f}")
    data = open(f,'r').read()
    for i,line in enumerate(data.splitlines(),1):
        if 'sort((a, b) => a - b)' in line:
            print(f"{i}:{line}")
PY

Repository: Karanjot786/TermUI

Length of output: 15092


🏁 Script executed:

#!/bin/bash
set -euo pipefail

node - <<'JS'
const values = {
  zero: ['0', '0'],
  numerics: ['1', '10', '2'],
  packages: ['`@termuijs/core`', '`@termuijs/widgets`'],
  mixed_nonnumeric: ['abc', '1'],
};
for (const [name, arr] of Object.entries(values)) {
  const withSubtract = arr.slice().sort((a, b) => a - b);
  const withLocaleCompare = arr.slice().sort((a, b) => a.localeCompare(b));
  console.log(name, 'input', JSON.stringify(arr), 'subtract', JSON.stringify(withSubtract), 'localeCompare', JSON.stringify(withLocaleCompare));
}
JS

TS_VERSION=$(npx tsc --version 2>/dev/null | awk '{print $2}' || true)
tmpdir=${PWD}/tmp-ts-string-subtract-probe
rm -rf "$tmpdir"
mkdir -p "$tmpdir"
cat > "$tmpdir/comparators.ts" <<'TS'
function subtract(a: string, b: string): number { return a - b; }
subtract('0', '0');
subtract('1', '10');
function localeCompare(a: string, b: string): number { return a.localeCompare(b); }
localeCompare('0', '0');
localeCompare('1', '10');
TS
cat > "$tmpdir/tsconfig.json" <<'JSON'
{
  "compilerOptions": {
    "target": "ES2022",
    "moduleResolution": "bundler",
    "lib": ["ES2022"],
    "strict": true,
    "noEmit": true
  },
  "files": ["comparators.ts"]
}
JSON
cd "$tmpdir"
timeout 60 npx tsc --showConfig 2>/dev/null | sed -n '1,120p' || true
echo "--- tsc output -----------------------"
timeout 60 npx tsc 2>&1 | sed -n '1,200p' || true
rm -rf "$tmpdir"

Repository: Karanjot786/TermUI

Length of output: 621


🏁 Script executed:

#!/bin/bash
set -euo pipefail

node - <<'JS'
const values = {
  zero: ['0', '0'],
  numerics: ['1', '10', '2'],
  packages: ['`@termuijs/core`', '`@termuijs/widgets`'],
  mixed_nonnumeric: ['abc', '1'],
};
for (const [name, arr] of Object.entries(values)) {
  const withSubtract = arr.slice().sort((a, b) => a - b);
  const withLocaleCompare = arr.slice().sort((a, b) => a.localeCompare(b));
  console.log(name, 'input', JSON.stringify(arr), 'subtract', JSON.stringify(withSubtract), 'localeCompare', JSON.stringify(withLocaleCompare));
}
JS

TS_VERSION=$(npx tsc --version 2>/dev/null | awk '{print $2}' || true)
tmpdir=${PWD}/tmp-ts-string-subtract-probe
rm -rf "$tmpdir"
mkdir -p "$tmpdir"
cat >("$tmpdir/comparators.ts") <<'TS'
function subtract(a: string, b: string): number { return a - b; }
subtract('0', '0');
subtract('1', '10');
function localeCompare(a: string, b: string): number { return a.localeCompare(b); }
localeCompare('0', '0');
localeCompare('1', '10');
TS
cat >"$tmpdir/tsconfig.json" <<'JSON'
{
  "compilerOptions": {
    "target": "ES2022",
    "moduleResolution": "bundler",
    "lib": ["ES2022"],
    "strict": true,
    "noEmit": true
  },
  "files": ["comparators.ts"]
}
JSON
cd "$tmpdir"
timeout 60 npx tsc --showConfig 2>/dev/null | sed -n '1,120p' || true
echo "--- tsc output -----------------------"
timeout 60 npx tsc 2>&1 | sed -n '1,200p' || true
rm -rf "$tmpdir"

Repository: Karanjot786/TermUI

Length of output: 688


Use string comparators instead of subtraction.

Both comparators operate on string values, and subtraction produces numeric ordering instead of the intended string order for nonnumeric values such as ['abc', '1']. Use String.prototype.localeCompare; for scripts/build-registry.ts, the default string sort is also sufficient.

🧰 Tools
🪛 GitHub Actions: CI / 0_build-and-test.txt

[error] 185-186: TypeScript declaration build failed: arithmetic operands on lines 185-186 are not typed as any, number, bigint, or an enum (TS2362, TS2363). The '@termuijs/ui' build command 'bun run build' / 'tsup' exited with code 1.

🪛 GitHub Actions: CI / build-and-test

[error] 185-186: TypeScript declaration build failed: arithmetic operands on lines 185-186 are not typed as any, number, bigint, or an enum type (TS2362/TS2363). The @termuijs/ui build command exited with code 1.

📍 Affects 2 files
  • packages/ui/src/TreeSelect.ts#L185-L186 (this comment)
  • scripts/build-registry.ts#L47-L47
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/TreeSelect.ts` around lines 185 - 186, Replace the
subtraction comparators in TreeSelect’s sortedA and sortedB sorting with
String.prototype.localeCompare to preserve string ordering for all values. Also
update the sorting expression at scripts/build-registry.ts line 47 to use the
default string sort, as no numeric comparator is appropriate there.

Source: Coding guidelines

for (let i = 0; i < sortedA.length; i++) {
if (sortedA[i] !== sortedB[i]) return false;
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/build-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function collectDeps(content: string): string[] {
const deps = new Set<string>();
let m: RegExpExecArray | null;
while ((m = re.exec(content)) !== null) deps.add(m[1]!);
return [...deps].sort();
return [...deps].sort((a, b) => a - b);
}

export function toSlug(name: string): string {
Expand Down
Loading