Skip to content

Commit 8cc6f3a

Browse files
committed
feat: add id and validate utils
1 parent 594d880 commit 8cc6f3a

4 files changed

Lines changed: 190 additions & 2 deletions

File tree

package-lock.json

Lines changed: 30 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@
3434
"dependencies": {
3535
"abort-controller": "^3.0.0",
3636
"mysql2": "^3.14.3",
37+
"nanoid": "^5.1.5",
3738
"node-fetch": "^3.3.2",
38-
"pino": "^9.7.0"
39+
"pino": "^9.7.0",
40+
"ulid": "^3.0.1"
3941
},
4042
"devDependencies": {
4143
"@types/node": "^24.2.0",

src/utils/id.utils.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { randomUUID } from "crypto";
2+
import { ulid } from "ulid";
3+
import { nanoid } from "nanoid";
4+
5+
/**
6+
* Generates a UUID v4 string (RFC 4122).
7+
*
8+
* @returns {string} UUID v4 (e.g., 'c0de1234-5678-9abc-def0-123456789abc')
9+
*/
10+
export function uuid(): string {
11+
return randomUUID();
12+
}
13+
14+
/**
15+
* Generates a ULID (Universally Unique Lexicographically Sortable Identifier).
16+
*
17+
* @returns {string} ULID string (26 chars, Crockford Base32, e.g., '01H7ZXS9FJKPX06P1AYZKCGHQF').
18+
*/
19+
export function ulidString(): string {
20+
return ulid();
21+
}
22+
23+
/**
24+
* Generates a nanoid string (URL-friendly, collision-resistant, customizable size).
25+
*
26+
* @param {number} [size=21] - Number of characters for the ID (default: 21).
27+
* @returns {string} nanoid string.
28+
*/
29+
export function nanoId(size: number = 21): string {
30+
return nanoid(size);
31+
}
32+
33+
/**
34+
* Generates a cryptographically strong random hex string.
35+
*
36+
* @param {number} byteLength - Number of random bytes (default: 16 → 32 hex chars).
37+
* @returns {string} Random hex string.
38+
*/
39+
export function randomHex(byteLength: number = 16): string {
40+
// Node: crypto.getRandomValues is not available; use randomBytes if needed.
41+
// If using browser target, replace this impl accordingly.
42+
return Buffer.from(crypto.getRandomValues(new Uint8Array(byteLength)))
43+
.toString("hex")
44+
.slice(0, byteLength * 2);
45+
}
46+
47+
/**
48+
* Generates a random integer between min (inclusive) and max (inclusive).
49+
*
50+
* @param {number} min - Minimum value.
51+
* @param {number} max - Maximum value.
52+
* @returns {number} Random integer in range.
53+
*/
54+
export function randomInt(min: number, max: number): number {
55+
const range = max - min + 1;
56+
return Math.floor(Math.random() * range) + min;
57+
}

src/utils/validate.utils.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* Collection of common validation helpers for strings, numbers, email, UUID, etc.
3+
*/
4+
5+
/**
6+
* Checks if a string is a valid email address.
7+
*
8+
* @param {string} str - The input string.
9+
* @returns {boolean} True if valid email, else false.
10+
*/
11+
export function isEmail(str: string): boolean {
12+
// RFC 5322 official standard is more complex—this is practical.
13+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
14+
}
15+
16+
/**
17+
* Checks if a string is a valid UUID (versions 1-5).
18+
*
19+
* @param {string} str - The input string.
20+
* @returns {boolean} True if valid UUID, else false.
21+
*/
22+
export function isUUID(str: string): boolean {
23+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
24+
str,
25+
);
26+
}
27+
28+
/**
29+
* Checks if a string is a valid URL.
30+
*
31+
* @param {string} str - The input string.
32+
* @returns {boolean} True if valid URL, else false.
33+
*/
34+
export function isURL(str: string): boolean {
35+
try {
36+
new URL(str);
37+
return true;
38+
} catch {
39+
return false;
40+
}
41+
}
42+
43+
/**
44+
* Checks if a string is a valid international phone number (E.164 or common patterns).
45+
*
46+
* @param {string} str - The input string.
47+
* @returns {boolean} True if looks like a phone number.
48+
*/
49+
export function isPhone(str: string): boolean {
50+
// E.164 or basic with spaces, dashes, parentheses
51+
return /^(\+?[1-9]\d{1,14}|(\+?\d{1,3}[-.\s]?)?\(?\d{2,4}\)?([-\s]?\d{3,4}){2,})$/.test(
52+
str,
53+
);
54+
}
55+
56+
/**
57+
* Checks if a string is strictly alphanumeric (letters/numbers only).
58+
*
59+
* @param {string} str - The input string.
60+
* @returns {boolean} True if alphanumeric.
61+
*/
62+
export function isAlphanumeric(str: string): boolean {
63+
return /^[a-z0-9]+$/i.test(str);
64+
}
65+
66+
/**
67+
* Checks if a string or number can be safely parsed to a number.
68+
*
69+
* @param {string | number} value - The value to check.
70+
* @returns {boolean} True if the value is numeric.
71+
*/
72+
export function isNumeric(value: string | number): boolean {
73+
if (typeof value === "number") return !isNaN(value) && isFinite(value);
74+
return (
75+
typeof value === "string" && value.trim() !== "" && !isNaN(Number(value))
76+
);
77+
}
78+
79+
/**
80+
* Checks if a string is a valid hex color code (e.g. #FFF or #FFFFFF).
81+
*
82+
* @param {string} str - Input string.
83+
* @returns {boolean}
84+
*/
85+
export function isHexColor(str: string): boolean {
86+
return /^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(str);
87+
}
88+
89+
/**
90+
* Checks if a string is a valid ISO8601 date string.
91+
*
92+
* @param {string} str - Input string.
93+
* @returns {boolean}
94+
*/
95+
export function isISODate(str: string): boolean {
96+
// YYYY-MM-DD, extended, or with time portion
97+
return /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(?:\.\d+)?(Z|[\+|-]\d{2}:\d{2})?)?$/.test(
98+
str,
99+
);
100+
}

0 commit comments

Comments
 (0)