Summary
The ID Generator accepts an arbitrary custom NanoID alphabet but passes it directly to a byte-based sampling implementation with no validation. Several user-reachable inputs either throw during generation or produce identifiers from a different alphabet/entropy space than the UI implies.
Confirmed defects
1. Empty alphabet throws a runtime exception
For an empty alphabet, the mask becomes -1 and the calculated step becomes -Infinity. Generation then executes:
which throws RangeError: Invalid typed array length: -Infinity.
Because generation runs from a React effect, clearing the alphabet can break the tool flow rather than produce a field-level validation error.
2. Alphabets longer than 256 characters are not sampled fully
The implementation draws Uint8Array values, so each random value is limited to 0..255. When the alphabet contains more than 256 entries, indices after 255 are unreachable even though the mask calculation can be larger.
The displayed alphabet is therefore not the actual sampling alphabet.
3. Duplicate characters bias the output space
Duplicate alphabet entries occupy multiple indices and are selected more often than unique entries. This reduces the effective identifier entropy and makes collision expectations different from a unique-alphabet calculation.
4. Non-BMP Unicode alphabets are split into UTF-16 code units
The code uses alphabet.length and alphabet[index]. An alphabet containing emoji or other non-BMP symbols can select individual surrogate halves and emit malformed text instead of complete symbols.
5. Size is not validated before allocation/generation
The number input uses Number(event.target.value) directly. Blank/zero values can produce empty identifiers; negative or non-finite values can result in invalid allocation/math paths despite the HTML min/max attributes.
Why this matters
NanoID is selected specifically for compact, collision-resistant identifiers. Silent alphabet truncation, biased sampling, malformed symbols, or empty output invalidates that purpose while the generated strings may still look usable.
HTML input constraints are not a substitute for validating state in the generation function.
Recommended implementation
Extract the ID algorithms from the page into a tested logic.ts module and validate before generation.
Choose and document one custom-alphabet policy:
- restrict to 2-256 unique ASCII/single-byte symbols; or
- implement code-point-aware sampling with a random source/algorithm that supports the documented upper bound.
For the current byte-based algorithm, the simpler safe contract is 2-256 unique code points with duplicates rejected or explicitly deduplicated after warning the user.
Also validate quantity and NanoID size as finite integers within their UI limits before allocating or looping.
Acceptance criteria
Relevant files
src/features/tools/id-generator/page.tsx
- new/extracted
src/features/tools/id-generator/logic.ts
- new unit/component tests for ID Generator
Summary
The ID Generator accepts an arbitrary custom NanoID alphabet but passes it directly to a byte-based sampling implementation with no validation. Several user-reachable inputs either throw during generation or produce identifiers from a different alphabet/entropy space than the UI implies.
Confirmed defects
1. Empty alphabet throws a runtime exception
For an empty alphabet, the mask becomes
-1and the calculated step becomes-Infinity. Generation then executes:which throws
RangeError: Invalid typed array length: -Infinity.Because generation runs from a React effect, clearing the alphabet can break the tool flow rather than produce a field-level validation error.
2. Alphabets longer than 256 characters are not sampled fully
The implementation draws
Uint8Arrayvalues, so each random value is limited to0..255. When the alphabet contains more than 256 entries, indices after 255 are unreachable even though the mask calculation can be larger.The displayed alphabet is therefore not the actual sampling alphabet.
3. Duplicate characters bias the output space
Duplicate alphabet entries occupy multiple indices and are selected more often than unique entries. This reduces the effective identifier entropy and makes collision expectations different from a unique-alphabet calculation.
4. Non-BMP Unicode alphabets are split into UTF-16 code units
The code uses
alphabet.lengthandalphabet[index]. An alphabet containing emoji or other non-BMP symbols can select individual surrogate halves and emit malformed text instead of complete symbols.5. Size is not validated before allocation/generation
The number input uses
Number(event.target.value)directly. Blank/zero values can produce empty identifiers; negative or non-finite values can result in invalid allocation/math paths despite the HTMLmin/maxattributes.Why this matters
NanoID is selected specifically for compact, collision-resistant identifiers. Silent alphabet truncation, biased sampling, malformed symbols, or empty output invalidates that purpose while the generated strings may still look usable.
HTML input constraints are not a substitute for validating state in the generation function.
Recommended implementation
Extract the ID algorithms from the page into a tested
logic.tsmodule and validate before generation.Choose and document one custom-alphabet policy:
For the current byte-based algorithm, the simpler safe contract is 2-256 unique code points with duplicates rejected or explicitly deduplicated after warning the user.
Also validate quantity and NanoID size as finite integers within their UI limits before allocating or looping.
Acceptance criteria
Relevant files
src/features/tools/id-generator/page.tsxsrc/features/tools/id-generator/logic.ts