input-sense
A lightweight JavaScript utility that detects low-quality, fake, or placeholder user inputs that pass traditional regex validation.
It adds a human-intent layer on top of normal validation.
Why input-sense
Most validation libraries focus on syntax, not intent.
Inputs like:
aaaatest123456qwertyasdfπππpassword
often pass validation even though they are meaningless.
input-sense helps detect such inputs early and improve overall data quality.
Features
- Detects repeated characters
- Flags placeholder words (38 built-in + custom words support)
- Detects sequential patterns
- Detects reverse sequences
- Identifies keyboard patterns
- Detects all-caps inputs
- Detects unicode/emoji-only inputs
- Detects leet speak placeholder variants (e.g.
4dmin,t3st) - Input type presets β smart rule configuration for
email,fullName,pin,phone, andpasswordfields - Supports returning all detected issues (
mode: "all") - Supports structured output with rule names (
mode: "detailed") - Supports quality scoring (
mode: "score") - Allows enabling/disabling specific rules
- Supports custom rule execution order via
priorityoption - Supports per-rule configuration for fine-grained tuning
- Batch validation for multiple form fields (
senseInputBatch) - Runtime rule discovery (
listRules) - Ships as both ESM and CommonJS
- Lightweight and dependency-free
- Frontend-friendly
- Full TypeScript support with per-mode return type inference
- Includes automated tests for core validation logic
- Detects symbol-only or whitespace inputs
- Detects numeric-only inputs
- Detects repeated word patterns
- Detects low vowel ratio (non-human-like input)
Installation
Using npm:
npm install input-sense
Using yarn:
yarn add input-sense
Using pnpm:
pnpm add input-sense
Usage
import { senseInput } from "input-sense";
const result = senseInput("qwerty");
if (result) {
console.log(result);
} else {
console.log("Input looks valid");
}
CommonJS is also supported:
const { senseInput } = require("input-sense");
Input Type Presets
Use the type option to automatically apply the right rules for a specific form field.
No need to manually configure disable, rules, or priority β the preset handles it.
type: "email"
Validates email inputs with comprehensive real-world checks:
senseInput("harshit@gmail.com", { type: "email" })
// null β valid email
senseInput("test@gmail.com", { type: "email" })
// "Email local part looks like a placeholder"
senseInput("user@mailinator.com", { type: "email" })
// "Email domain looks disposable or fake"
senseInput("noreply@gmail.com", { type: "email" })
// "Email local part looks like a system address"
senseInput("notanemail", { type: "email" })
// "Input does not look like a valid email address"
senseInput("1234@gmail.com", { type: "email" })
// "Input does not look like a valid email address"
senseInput("user@gmai1.com", { type: "email" })
// "Input does not look like a valid email address"
senseInput("test+123@gmail.com", { type: "email" })
// "Email local part looks like a placeholder"
senseInput("harshit+work@gmail.com", { type: "email" })
// null β legitimate plus tag
Email config options:
// Only allow specific domains
senseInput("user@gmail.com", {
type: "email",
rules: {
validEmailFormat: {
allowedDomains: ["company.com", "company.co.in"]
}
}
})
// "Email domain is not accepted"
// Block additional domains
senseInput("user@competitor.com", {
type: "email",
rules: {
validEmailFormat: {
blockedDomains: ["competitor.com"]
}
}
})
// "Email domain looks disposable or fake"
What email validation covers:
- Format:
@count, local part 2β64 chars, total max 254 chars - No all-numeric local parts, no consecutive dots, no leading/trailing dots
- Domain name cannot contain digits (catches
gmai1.com,g00gle.com) - Domain cannot start or end with hyphens
- 33 built-in blocked disposable domains
- Blocked system addresses:
noreply,postmaster,support,admin,infoand more - Placeholder detection in local part segments (
test.user@,demo_app@) - Plus-tag placeholder detection
type: "fullName"
Validates full name inputs requiring first and last name:
senseInput("Harshit Patle", { type: "fullName" })
// null β valid full name
senseInput("Harshit", { type: "fullName" })
// "Please enter your full name (first and last name)"
senseInput("HARSHIT PATLE", { type: "fullName" })
// "Input contains only uppercase letters and looks non-meaningful"
senseInput("J Smith", { type: "fullName" })
// "Each part of your name must be at least 2 characters"
senseInput("John 123", { type: "fullName" })
// "Name parts must contain only letters"
senseInput("Mary-Jane Watson", { type: "fullName" })
// null β hyphenated names supported
senseInput("O'Brien Connor", { type: "fullName" })
// null β apostrophe names supported
senseInput("MarΓa GarcΓa", { type: "fullName" })
// null β accented characters supported
senseInput("St. John Smith", { type: "fullName" })
// null β dotted prefixes supported
senseInput("john smith", { type: "fullName" })
// null β casing is not enforced
What fullName validation covers:
- Must contain a space (first and last name required)
- Each name part minimum 2 characters
- No digits in any name part
- Supports hyphens, apostrophes, dots in names
- Supports Unicode/accented characters
- Normalizes multiple spaces automatically
- Maximum 100 characters total
- Catches placeholder words, repeated chars, all-caps, keyboard patterns
Disable space requirement for mononyms:
senseInput("Beyonce", {
type: "fullName",
disable: ["spaceRequired"]
})
// null
type: "pin"
Validates PIN inputs for numeric-only fields:
senseInput("1478", { type: "pin" })
// null β valid PIN
senseInput("123", { type: "pin" })
// "PIN must be at least 4 digits"
senseInput("1111", { type: "pin" })
// "PIN must not contain repeated digits"
senseInput("1234", { type: "pin" })
// null β sequential digits allowed by default
senseInput("147258", { type: "pin" })
// null β valid 6-digit PIN
senseInput("1234567", { type: "pin" })
// "PIN must be at most 6 digits"
senseInput("abcd", { type: "pin" })
// "PIN must contain only numbers"
PIN config options:
// Disallow sequential digits
senseInput("1234", {
type: "pin",
rules: { pinRule: { noSequential: true } }
})
// "PIN must not contain sequential digits"
// Allow repeated digits
senseInput("1111", {
type: "pin",
rules: { pinRule: { noRepeated: false } }
})
// null
What PIN validation covers:
- Numeric-only (no letters, no special characters)
- Default length: 4β6 digits (configurable)
- No repeated digits (configurable, default: true)
- No sequential digits (configurable, default: false)
- Configurable min/max length
type: "phone"
Validates phone numbers with international format support:
senseInput("1234567890", { type: "phone" })
// null β valid 10-digit phone
senseInput("(123) 456-7890", { type: "phone" })
// null β formatted phone
senseInput("+1 234-567-8900", { type: "phone" })
// null β international format
senseInput("+91 98765 43210", { type: "phone" })
// null β India format
senseInput("123", { type: "phone" })
// "Phone number must contain at least 10 digits"
senseInput("abc123", { type: "phone" })
// "Phone number must contain only numbers and valid separators"
senseInput("1111111111", { type: "phone" })
// null β repeated digits allowed by default
Phone config options:
// Disallow repeated digits
senseInput("1111111111", {
type: "phone",
rules: { phoneRule: { noRepeated: true } }
})
// "Phone number must not contain repeated digits"
// Custom digit range
senseInput("12345", {
type: "phone",
rules: { phoneRule: { minDigits: 5, maxDigits: 15 } }
})
// null
What phone validation covers:
- Digit count: 10β15 by default (configurable)
- Allows separators:
+, spaces, dashes, parentheses (configurable) - No letters allowed
- Repeated digits check (configurable, default: false)
- International format support
type: "password"
Validates password strength with comprehensive checks:
senseInput("Harshit@123", { type: "password" })
// null β strong password
senseInput("harshit", { type: "password" })
// "Password must be at least 8 characters"
senseInput("harshit123", { type: "password" })
// "Password must contain at least 1 uppercase letter"
senseInput("HARSHIT123", { type: "password" })
// "Password must contain at least 1 lowercase letter"
senseInput("Harshit@", { type: "password" })
// "Password must contain at least 1 number"
senseInput("Harshit123", { type: "password" })
// "Password must contain at least 1 special character"
senseInput("password", { type: "password" })
// "Password is too common and easily guessable"
senseInput("P@ssw0rd", { type: "password" })
// "Password is too common and easily guessable"
Password config options:
// Custom minimum length
senseInput("H@1", {
type: "password",
rules: { passwordStrength: { minLength: 3, requireSpecial: true } }
})
// null
// Skip uppercase requirement
senseInput("harshit@123", {
type: "password",
rules: { passwordStrength: { requireUppercase: false } }
})
// null
// Skip common password check
senseInput("password123", {
type: "password",
rules: { passwordStrength: { checkCommon: false } }
})
// null
What password validation covers:
- Minimum length: 8 by default (configurable)
- At least 1 uppercase letter (configurable)
- At least 1 lowercase letter (configurable)
- At least 1 number (configurable)
- At least 1 special character (configurable)
- Common password blacklist (50+ entries, configurable)
- Works alongside existing rules (repeatedChar, keyboardPattern, sequential, leetSpeak, entropy, minLength)
Advanced Usage
Get all detected issues
By default, input-sense returns only the first detected issue.
To get all detected issues, use mode: "all".
senseInput("aa", { mode: "all" });
Get structured output with rule names
Use mode: "detailed" to get back an array of { rule, message } objects.
This is useful when you want to show different UI feedback per rule.
senseInput("aaaa", { mode: "detailed" });
// [{ rule: "repeatedChar", message: "Input looks like repeated characters" }]
Get a quality score
Use mode: "score" to get a 0β100 quality score instead of pass/fail.
100 means fully clean, 0 means completely unusable.
senseInput("Harshit", { mode: "score" });
// 100
senseInput("aaaa", { mode: "score" });
// 0
Validate multiple fields at once
Use senseInputBatch to validate an entire form object in one call.
import { senseInputBatch } from "input-sense";
senseInputBatch({
email: "harshit@gmail.com",
fullName: "Harshit Patle"
}, { type: "email" });
Or validate different field types separately:
const emailResult = senseInput(emailValue, { type: "email" });
const nameResult = senseInput(nameValue, { type: "fullName" });
const pinResult = senseInput(pinValue, { type: "pin" });
const phoneResult = senseInput(phoneValue, { type: "phone" });
const passwordResult = senseInput(passwordValue, { type: "password" });
Control rule execution order
senseInput("aaaa", {
priority: ["minLength", "repeatedChar"],
rules: { minLength: { minLength: 5 } }
});
// "Input is too short to be meaningful (minimum 5 characters)"
Discover available rules at runtime
import { listRules } from "input-sense";
listRules();
// ["spaceRequired", "repeatedChar", "allCaps", "unicodeOnly", "symbolOnly",
// "numericOnly", "placeholderWord", "leetSpeak", "repeatedWord", "minLength",
// "sequential", "reverseSequential", "keyboardPattern", "entropy",
// "lowVowelRatio", "validEmailFormat", "namePartsRule", "pinRule",
// "phoneRule", "passwordStrength"]
Disable specific rules
senseInput("aa", {
mode: "all",
disable: ["repeatedChar"]
});
Rule Configuration Examples
repeatedChar β custom repetition threshold
senseInput("aaa", {
rules: { repeatedChar: { threshold: 4 } }
});
// null β only flags when 5+ repetitions
placeholderWord β add custom words
senseInput("mycustomword", {
rules: { placeholderWord: { customWords: ["mycustomword"] } }
});
// "Input looks like a placeholder word"
keyboardPattern β custom minimum length
senseInput("qw", {
rules: { keyboardPattern: { minLength: 5 } }
});
// null β input is too short to check
repeatedWord β allow some duplication
senseInput("hey hey there", {
rules: { repeatedWord: { maxAllowedRatio: 0.6 } }
});
// null
minLength β custom minimum length
senseInput("hello", {
rules: { minLength: { minLength: 6 } }
});
// "Input is too short to be meaningful (minimum 6 characters)"
pinRule β custom PIN config
senseInput("1234", {
type: "pin",
rules: { pinRule: { noSequential: true } }
});
// "PIN must not contain sequential digits"
phoneRule β custom phone config
senseInput("1111111111", {
type: "phone",
rules: { phoneRule: { noRepeated: true } }
});
// "Phone number must not contain repeated digits"
passwordStrength β custom password config
senseInput("Harshit123", {
type: "password",
rules: { passwordStrength: { requireSpecial: false } }
});
// null
When to use each mode
| Mode | Returns | Use when |
|---|---|---|
"first" (default) |
string | null |
You only need the first blocking issue |
"all" |
string[] | null |
You want to show all issues at once |
"detailed" |
{ rule, message }[] | null |
You need to know which rule fired |
"score" |
number |
You want a 0β100 quality score |
Rule Configuration Reference
| Rule | Config option | Type | Default | Description |
|---|---|---|---|---|
repeatedChar |
threshold |
number |
0 |
Min repetitions before flagging |
allCaps |
minLength |
number |
4 |
Min length before checking |
unicodeOnly |
minLength |
number |
1 |
Min length before checking |
placeholderWord |
customWords |
string[] |
[] |
Extra words to flag |
leetSpeak |
customWords |
string[] |
[] |
Extra decoded words to flag |
keyboardPattern |
minLength |
number |
3 |
Min length before checking |
repeatedWord |
maxAllowedRatio |
number |
0 |
Max allowed duplication ratio |
lowVowelRatio |
minLength |
number |
5 |
Min length before checking |
lowVowelRatio |
minRatio |
number |
0.2 |
Min vowel ratio required |
minLength |
minLength |
number |
4 |
Min required input length |
entropy |
minLength |
number |
6 |
Min length before checking |
entropy |
minRatio |
number |
0.6 |
Min character diversity ratio |
validEmailFormat |
allowedDomains |
string[] |
[] |
Whitelist specific domains |
validEmailFormat |
blockedDomains |
string[] |
[] |
Extra domains to block |
pinRule |
minLength |
number |
4 |
Min PIN length |
pinRule |
maxLength |
number |
6 |
Max PIN length |
pinRule |
noRepeated |
boolean |
true |
Disallow repeated digits |
pinRule |
noSequential |
boolean |
false |
Disallow sequential digits |
phoneRule |
minDigits |
number |
10 |
Min digit count |
phoneRule |
maxDigits |
number |
15 |
Max digit count |
phoneRule |
allowPlus |
boolean |
true |
Allow + sign |
phoneRule |
allowSpaces |
boolean |
true |
Allow spaces |
phoneRule |
allowDashes |
boolean |
true |
Allow dashes |
phoneRule |
allowParens |
boolean |
true |
Allow parentheses |
phoneRule |
noRepeated |
boolean |
false |
Disallow repeated digits |
passwordStrength |
minLength |
number |
8 |
Min password length |
passwordStrength |
requireUppercase |
boolean |
true |
Require uppercase |
passwordStrength |
requireLowercase |
boolean |
true |
Require lowercase |
passwordStrength |
requireNumber |
boolean |
true |
Require number |
passwordStrength |
requireSpecial |
boolean |
true |
Require special char |
passwordStrength |
checkCommon |
boolean |
true |
Check common passwords |
All rules have safe defaults. Unknown rule names in rules config are safely ignored.
Example Outputs
senseInput("aaaa");
// "Input looks like repeated characters"
senseInput("HELLO");
// "Input contains only uppercase letters and looks non-meaningful"
senseInput("π₯π₯π₯");
// "Input contains no standard characters and looks non-meaningful"
senseInput("4dmin");
// "Input looks like a leet speak placeholder word"
senseInput("test");
// "Input looks like a placeholder word"
senseInput("1234");
// "Input looks like a sequential pattern"
senseInput("9876");
// "Input looks like a reverse sequential pattern"
senseInput("qwerty");
// "Input looks like a keyboard pattern"
senseInput("123456");
// "Input contains only numbers and looks non-meaningful"
senseInput("test test test");
// "Input contains repeated words and looks non-meaningful"
senseInput("bcdfgh");
// "Input has very low vowel presence and looks non-meaningful"
senseInput("Harshit");
// null
Test Coverage
This project uses automated test coverage to ensure reliability. Coverage is collected and reported on every commit via CI. All rule files maintain 100% coverage across statements, branches, functions, and lines.
Mental Model
Think of input-sense as an intent checker, not a validator.
It runs a series of small, focused rules to answer one question:
"Does this input look meaningful for a human?"
- Each rule checks a specific pattern
- Rules run in a configurable order
- Type presets automatically apply the right rules for each field
- By default, the first issue is returned
- You can opt into collecting all issues, structured output, or a quality score
How it works
Validation flow
User Input
β
input-sense
β’ Regex validation (minLength, numericOnly, sequential patterns, etc.)
β’ Intent detection (placeholder words, disposable emails, leet-speak, etc.)
β’ Optional type presets (email, fullName, pin, phone, password)
β
Pass β valid input
Fail β descriptive error message
What input-sense does NOT do
- Does not replace backend validation
- Does not replace regex validation
- Does not block submissions automatically
- Does not use AI
- Does not store user data
Limitations
- This library does not guarantee semantic correctness
- It does not understand language meaning
- It should be used alongside traditional validation
Use Cases
- Signup and login forms
- Admin dashboards
- Hackathon projects
- Educational platforms
- Frontend UX improvement
- Pre-API input sanity checks
- PIN entry forms
- Phone number collection
- Password strength indicators
Project Status
This project is actively maintained. All core validation rules are covered by automated tests and enforced via CI.
Quick Guide
| Use case | Recommendation |
|---|---|
| Email field | type: "email" |
| Full name field | type: "fullName" |
| PIN field | type: "pin" |
| Phone field | type: "phone" |
| Password field | type: "password" |
| Simple forms | Default mode |
| Rich UX | mode: "all" |
| Rule-specific UX | mode: "detailed" |
| Live quality indicator | mode: "score" |
| Full form validation | senseInputBatch |
| Custom rule order | priority option |
| Strict apps | Enable entropy tuning |
| JS projects | ESM or CommonJS |
| TS projects | Enjoy full type safety |
Author
License
This project is licensed under the MIT License. See the LICENSE file for details.