@kumix/utils 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js ADDED
@@ -0,0 +1,555 @@
1
+ import { t as logger } from "./logger-BOB35dQN.js";
2
+ import { randomBytes, randomInt } from "node:crypto";
3
+ import jwt from "jsonwebtoken";
4
+ import bcrypt from "bcryptjs";
5
+ import * as chrono from "chrono-node";
6
+ //#region src/functions/crypto/generate-random-string.ts
7
+ /**
8
+ * Utility function for generating cryptographically secure random strings
9
+ * Provides secure random string generation for tokens, IDs, and other security-sensitive uses
10
+ */
11
+ /**
12
+ * Generates a cryptographically secure random string of specified length
13
+ * Uses Node.js crypto module for secure random number generation
14
+ *
15
+ * @param length - The length of the random string to generate
16
+ * @returns A random string consisting of uppercase letters and numbers
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { generateRandomString } from '@/utils/functions';
21
+ *
22
+ * // Generate a 6-character random string
23
+ * const verificationCode = generateRandomString(6);
24
+ * // Example output: "A7B3C9"
25
+ *
26
+ * // Generate a longer random string for tokens
27
+ * const apiToken = generateRandomString(32);
28
+ * // Example output: "X7F9A2B5C8D1E4F7G0H3I6J9K2L5M8N1"
29
+ *
30
+ * // Use as unique identifiers
31
+ * const uniqueId = generateRandomString(12);
32
+ * // Example output: "B7C9D1E3F5G7"
33
+ * ```
34
+ */
35
+ function generateRandomString(length) {
36
+ const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
37
+ const randomBytesArray = randomBytes(length);
38
+ let result = "";
39
+ for (let i = 0; i < length; i++) {
40
+ const randomIndex = randomBytesArray[i] % 36;
41
+ result += charset[randomIndex];
42
+ }
43
+ return result;
44
+ }
45
+ //#endregion
46
+ //#region src/functions/crypto/jwt.ts
47
+ /**
48
+ * JWT (JSON Web Token) utilities for SaaS applications
49
+ * Provides secure token generation, verification, and management
50
+ * Includes validation and error handling for production use
51
+ */
52
+ /**
53
+ * Generates a JWT token with the provided payload
54
+ *
55
+ * @param payload - The data to encode in the token
56
+ * @param secret - The secret key for signing the token
57
+ * @param options - Token generation options
58
+ * @returns The generated JWT token or null if failed
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * import { generateJWT } from '@kumix/utils';
63
+ *
64
+ * // Basic usage
65
+ * const token = generateJWT(
66
+ * { userId: '123', email: 'user@example.com' },
67
+ * process.env.JWT_SECRET
68
+ * );
69
+ *
70
+ * // With custom expiration
71
+ * const token = generateJWT(
72
+ * { userId: '123', email: 'user@example.com', role: 'admin' },
73
+ * process.env.JWT_SECRET,
74
+ * { expiresIn: '1h' }
75
+ * );
76
+ *
77
+ * // For API authentication
78
+ * const authToken = generateJWT(
79
+ * { userId: user.id, email: user.email, workspaceId: workspace.id },
80
+ * process.env.JWT_SECRET,
81
+ * { expiresIn: '24h', issuer: 'myapp.com' }
82
+ * );
83
+ * ```
84
+ */
85
+ function generateJWT(payload, secret, options = {}) {
86
+ if (!payload || typeof payload !== "object") {
87
+ logger.warn("JWT generation: Payload must be a valid object");
88
+ return null;
89
+ }
90
+ if (!payload.userId || typeof payload.userId !== "string") {
91
+ logger.warn("JWT generation: userId is required in payload");
92
+ return null;
93
+ }
94
+ if (!payload.email || typeof payload.email !== "string") {
95
+ logger.warn("JWT generation: email is required in payload");
96
+ return null;
97
+ }
98
+ if (!secret || typeof secret !== "string") {
99
+ logger.warn("JWT generation: Secret must be a non-empty string");
100
+ return null;
101
+ }
102
+ const { expiresIn = "7d", issuer, audience } = options;
103
+ const { exp, ...cleanPayload } = payload;
104
+ try {
105
+ const signOptions = {
106
+ ...typeof exp === "undefined" ? { expiresIn } : {},
107
+ ...issuer && { issuer },
108
+ ...audience && { audience }
109
+ };
110
+ return jwt.sign(cleanPayload, secret, signOptions);
111
+ } catch (error) {
112
+ logger.error("JWT generation: Failed to generate token", { error });
113
+ return null;
114
+ }
115
+ }
116
+ /**
117
+ * Verifies and decodes a JWT token
118
+ *
119
+ * @param token - The JWT token to verify
120
+ * @param secret - The secret key used to sign the token
121
+ * @param options - Verification options
122
+ * @returns Verification result with payload if valid
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * import { verifyJWT } from '@kumix/utils';
127
+ *
128
+ * // Basic usage
129
+ * const result = verifyJWT(token, process.env.JWT_SECRET);
130
+ * if (result.isValid && result.payload) {
131
+ * const { userId, email } = result.payload;
132
+ * // Proceed with authenticated user
133
+ * }
134
+ *
135
+ * // With issuer verification
136
+ * const result = verifyJWT(token, process.env.JWT_SECRET, {
137
+ * issuer: 'myapp.com'
138
+ * });
139
+ *
140
+ * // In authentication middleware
141
+ * async function authenticateRequest(req, res, next) {
142
+ * const token = req.headers.authorization?.replace('Bearer ', '');
143
+ * const result = verifyJWT(token, process.env.JWT_SECRET);
144
+ *
145
+ * if (!result.isValid) {
146
+ * return res.status(401).json({ error: 'Invalid token' });
147
+ * }
148
+ *
149
+ * req.user = result.payload;
150
+ * next();
151
+ * }
152
+ * ```
153
+ */
154
+ function verifyJWT(token, secret, options = {}) {
155
+ if (!token || typeof token !== "string") return {
156
+ isValid: false,
157
+ error: "Token must be a non-empty string"
158
+ };
159
+ if (!secret || typeof secret !== "string") return {
160
+ isValid: false,
161
+ error: "Secret must be a non-empty string"
162
+ };
163
+ const { issuer, audience } = options;
164
+ try {
165
+ return {
166
+ isValid: true,
167
+ payload: jwt.verify(token, secret, {
168
+ ...issuer && { issuer },
169
+ ...audience && { audience }
170
+ })
171
+ };
172
+ } catch (error) {
173
+ logger.warn("JWT verification: Token verification failed", { error });
174
+ if (error instanceof jwt.TokenExpiredError) return {
175
+ isValid: false,
176
+ error: "Token has expired"
177
+ };
178
+ if (error instanceof jwt.JsonWebTokenError) return {
179
+ isValid: false,
180
+ error: "Invalid token format"
181
+ };
182
+ return {
183
+ isValid: false,
184
+ error: "Token verification failed"
185
+ };
186
+ }
187
+ }
188
+ /**
189
+ * Decodes a JWT token without verification (for debugging/inspection)
190
+ *
191
+ * @param token - The JWT token to decode
192
+ * @returns Decoded payload or null if failed
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * import { decodeJWT } from '@kumix/utils';
197
+ *
198
+ * // Inspect token contents without verification
199
+ * const payload = decodeJWT(token);
200
+ * if (payload) {
201
+ * console.log('Token expires at:', new Date(payload.exp * 1000));
202
+ * console.log('User ID:', payload.userId);
203
+ * }
204
+ *
205
+ * // For debugging purposes
206
+ * function debugToken(token: string) {
207
+ * const payload = decodeJWT(token);
208
+ * if (payload) {
209
+ * console.log('Token payload:', payload);
210
+ * } else {
211
+ * console.log('Invalid token format');
212
+ * }
213
+ * }
214
+ * ```
215
+ */
216
+ function decodeJWT(token) {
217
+ if (!token || typeof token !== "string") return null;
218
+ try {
219
+ return jwt.decode(token);
220
+ } catch (error) {
221
+ logger.warn("JWT decode: Failed to decode token", { error });
222
+ return null;
223
+ }
224
+ }
225
+ /**
226
+ * Checks if a JWT token is expired
227
+ *
228
+ * @param token - The JWT token to check
229
+ * @returns True if token is expired, false otherwise
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * import { isJWTExpired } from '@kumix/utils';
234
+ *
235
+ * // Check if token needs refresh
236
+ * if (isJWTExpired(userToken)) {
237
+ * // Redirect to login or refresh token
238
+ * await refreshUserToken();
239
+ * }
240
+ *
241
+ * // In token refresh logic
242
+ * function shouldRefreshToken(token: string): boolean {
243
+ * return isJWTExpired(token);
244
+ * }
245
+ * ```
246
+ */
247
+ function isJWTExpired(token) {
248
+ const payload = decodeJWT(token);
249
+ if (!payload?.exp) return true;
250
+ const currentTime = Math.floor(Date.now() / 1e3);
251
+ return payload.exp < currentTime;
252
+ }
253
+ /**
254
+ * Gets the remaining time until token expiration
255
+ *
256
+ * @param token - The JWT token to check
257
+ * @returns Remaining time in seconds, or 0 if expired/invalid
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * import { getJWTTimeRemaining } from '@kumix/utils';
262
+ *
263
+ * // Check how much time is left
264
+ * const timeLeft = getJWTTimeRemaining(userToken);
265
+ * if (timeLeft < 300) { // Less than 5 minutes
266
+ * // Show warning about token expiration
267
+ * showTokenExpirationWarning();
268
+ * }
269
+ *
270
+ * // Format time remaining for display
271
+ * function formatTimeRemaining(token: string): string {
272
+ * const seconds = getJWTTimeRemaining(token);
273
+ * const minutes = Math.floor(seconds / 60);
274
+ * const hours = Math.floor(minutes / 60);
275
+ *
276
+ * if (hours > 0) return `${hours}h ${minutes % 60}m`;
277
+ * if (minutes > 0) return `${minutes}m`;
278
+ * return `${seconds}s`;
279
+ * }
280
+ * ```
281
+ */
282
+ function getJWTTimeRemaining(token) {
283
+ const payload = decodeJWT(token);
284
+ if (!payload?.exp) return 0;
285
+ const currentTime = Math.floor(Date.now() / 1e3);
286
+ const timeRemaining = payload.exp - currentTime;
287
+ return Math.max(0, timeRemaining);
288
+ }
289
+ //#endregion
290
+ //#region src/functions/crypto/password.ts
291
+ /**
292
+ * Password hashing and verification utilities for SaaS applications
293
+ * Provides secure password handling using bcrypt with proper salt rounds
294
+ * Includes validation and error handling for production use
295
+ */
296
+ /**
297
+ * Default salt rounds for bcrypt hashing
298
+ * 12 rounds provides good security while maintaining reasonable performance
299
+ * Higher values increase security but also increase computation time
300
+ */
301
+ const DEFAULT_SALT_ROUNDS = 12;
302
+ /**
303
+ * Hashes a password using bcrypt with salt
304
+ *
305
+ * @param password - The plain text password to hash
306
+ * @param options - Hashing options
307
+ * @returns Promise that resolves to the hashed password or null if failed
308
+ *
309
+ * @example
310
+ * ```typescript
311
+ * import { hashPassword } from '@kumix/utils';
312
+ *
313
+ * // Basic usage
314
+ * const hashedPassword = await hashPassword('userPassword123');
315
+ * if (hashedPassword) {
316
+ * // Store hashedPassword in database
317
+ * await saveUser({ email, password: hashedPassword });
318
+ * }
319
+ *
320
+ * // With custom salt rounds
321
+ * const hashedPassword = await hashPassword('userPassword123', {
322
+ * saltRounds: 14
323
+ * });
324
+ *
325
+ * // Error handling
326
+ * const hashedPassword = await hashPassword('userPassword123');
327
+ * if (!hashedPassword) {
328
+ * throw new Error('Failed to hash password');
329
+ * }
330
+ * ```
331
+ */
332
+ async function hashPassword(password, options = {}) {
333
+ if (!password || typeof password !== "string") {
334
+ logger.warn("Password hashing: Password must be a non-empty string");
335
+ return null;
336
+ }
337
+ if (password.length < 1) {
338
+ logger.warn("Password hashing: Password cannot be empty");
339
+ return null;
340
+ }
341
+ const { saltRounds = DEFAULT_SALT_ROUNDS } = options;
342
+ if (typeof saltRounds !== "number" || saltRounds < 4 || saltRounds > 20) {
343
+ logger.warn("Password hashing: Salt rounds must be between 4 and 20");
344
+ return null;
345
+ }
346
+ try {
347
+ const salt = await bcrypt.genSalt(saltRounds);
348
+ return await bcrypt.hash(password, salt);
349
+ } catch (error) {
350
+ logger.error("Password hashing: Failed to hash password", { error });
351
+ return null;
352
+ }
353
+ }
354
+ /**
355
+ * Verifies a password against its hash
356
+ *
357
+ * @param password - The plain text password to verify
358
+ * @param hashedPassword - The hashed password to compare against
359
+ * @returns Promise that resolves to verification result
360
+ *
361
+ * @example
362
+ * ```typescript
363
+ * import { verifyPassword } from '@kumix/utils';
364
+ *
365
+ * // Basic usage
366
+ * const result = await verifyPassword('userPassword123', storedHashedPassword);
367
+ * if (result.isValid) {
368
+ * // Password is correct, proceed with login
369
+ * await loginUser(user);
370
+ * } else {
371
+ * // Password is incorrect
372
+ * throw new Error('Invalid credentials');
373
+ * }
374
+ *
375
+ * // With error handling
376
+ * const result = await verifyPassword('userPassword123', storedHashedPassword);
377
+ * if (result.error) {
378
+ * logger.error('Password verification failed:', result.error);
379
+ * throw new Error('Authentication error');
380
+ * }
381
+ *
382
+ * // In authentication middleware
383
+ * async function authenticateUser(email: string, password: string) {
384
+ * const user = await getUserByEmail(email);
385
+ * if (!user) {
386
+ * return { success: false, error: 'User not found' };
387
+ * }
388
+ *
389
+ * const result = await verifyPassword(password, user.hashedPassword);
390
+ * if (!result.isValid) {
391
+ * return { success: false, error: 'Invalid password' };
392
+ * }
393
+ *
394
+ * return { success: true, user };
395
+ * }
396
+ * ```
397
+ */
398
+ async function verifyPassword(password, hashedPassword) {
399
+ if (!password || typeof password !== "string") return {
400
+ isValid: false,
401
+ error: "Password must be a non-empty string"
402
+ };
403
+ if (!hashedPassword || typeof hashedPassword !== "string") return {
404
+ isValid: false,
405
+ error: "Hashed password must be a non-empty string"
406
+ };
407
+ if (!hashedPassword.startsWith("$2a$") && !hashedPassword.startsWith("$2b$") && !hashedPassword.startsWith("$2y$")) return {
408
+ isValid: false,
409
+ error: "Invalid hash format"
410
+ };
411
+ try {
412
+ return { isValid: await bcrypt.compare(password, hashedPassword) };
413
+ } catch (error) {
414
+ logger.error("Password verification: Failed to verify password", { error });
415
+ return {
416
+ isValid: false,
417
+ error: "Password verification failed"
418
+ };
419
+ }
420
+ }
421
+ /**
422
+ * Checks if a password needs to be rehashed (e.g., due to updated salt rounds)
423
+ *
424
+ * @param hashedPassword - The current hashed password
425
+ * @param targetSaltRounds - The target salt rounds (default: 12)
426
+ * @returns Whether the password needs to be rehashed
427
+ *
428
+ * @example
429
+ * ```typescript
430
+ * import { needsRehash, hashPassword } from '@kumix/utils';
431
+ *
432
+ * // Check if password needs rehashing during login
433
+ * async function loginUser(email: string, password: string) {
434
+ * const user = await getUserByEmail(email);
435
+ * const result = await verifyPassword(password, user.hashedPassword);
436
+ *
437
+ * if (result.isValid) {
438
+ * // Check if we need to rehash with updated salt rounds
439
+ * if (needsRehash(user.hashedPassword, 14)) {
440
+ * const newHash = await hashPassword(password, { saltRounds: 14 });
441
+ * if (newHash) {
442
+ * await updateUserPassword(user.id, newHash);
443
+ * }
444
+ * }
445
+ *
446
+ * return { success: true, user };
447
+ * }
448
+ *
449
+ * return { success: false, error: 'Invalid credentials' };
450
+ * }
451
+ * ```
452
+ */
453
+ function needsRehash(hashedPassword, targetSaltRounds = DEFAULT_SALT_ROUNDS) {
454
+ if (!hashedPassword || typeof hashedPassword !== "string") return true;
455
+ try {
456
+ const parts = hashedPassword.split("$");
457
+ if (parts.length < 4) return true;
458
+ return parseInt(parts[2], 10) !== targetSaltRounds;
459
+ } catch (error) {
460
+ logger.warn("Password rehash check: Failed to parse hash", { error });
461
+ return true;
462
+ }
463
+ }
464
+ /**
465
+ * Generates a secure random password
466
+ *
467
+ * @param length - Length of the password (default: 16, min: 8, max: 128)
468
+ * @param options - Password generation options
469
+ * @returns Generated password string
470
+ *
471
+ * @example
472
+ * ```typescript
473
+ * import { generateSecurePassword } from '@kumix/utils';
474
+ *
475
+ * // Generate default password (16 characters)
476
+ * const password = generateSecurePassword();
477
+ *
478
+ * // Generate longer password
479
+ * const longPassword = generateSecurePassword(24);
480
+ *
481
+ * // Generate password without symbols
482
+ * const simplePassword = generateSecurePassword(12, {
483
+ * includeSymbols: false
484
+ * });
485
+ *
486
+ * // For temporary passwords
487
+ * const tempPassword = generateSecurePassword(12);
488
+ * await sendPasswordResetEmail(user.email, tempPassword);
489
+ * ```
490
+ */
491
+ function generateSecurePassword(length = 16, options = {}) {
492
+ const { includeUppercase = true, includeLowercase = true, includeNumbers = true, includeSymbols = true } = options;
493
+ const validLength = Math.max(8, Math.min(128, length));
494
+ let charset = "";
495
+ if (includeUppercase) charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
496
+ if (includeLowercase) charset += "abcdefghijklmnopqrstuvwxyz";
497
+ if (includeNumbers) charset += "0123456789";
498
+ if (includeSymbols) charset += "!@#$%^&*()_+-=[]{}|;:,.<>?";
499
+ if (charset.length === 0) charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
500
+ let password = "";
501
+ for (let i = 0; i < validLength; i++) {
502
+ const randomIndex = randomInt(0, charset.length);
503
+ password += charset[randomIndex];
504
+ }
505
+ return password;
506
+ }
507
+ //#endregion
508
+ //#region src/functions/datetime/parse-datetime.ts
509
+ /**
510
+ * Utility function for parsing date strings into Date objects
511
+ * Provides natural language date parsing capabilities
512
+ */
513
+ /**
514
+ * Parses a date string into a Date object using natural language processing
515
+ * Passes through Date objects unchanged
516
+ *
517
+ * @param str - The date string to parse or a Date object to pass through
518
+ * @returns A Date object, or null if parsing fails
519
+ *
520
+ * @example
521
+ * ```ts
522
+ * // Parse natural language date strings
523
+ * parseDateTime('tomorrow at 3pm')
524
+ * // Returns a Date object for tomorrow at 3:00 PM
525
+ *
526
+ * // Parse formal date strings
527
+ * parseDateTime('2023-06-15')
528
+ * // Returns a Date object for June 15, 2023
529
+ *
530
+ * // Parse relative dates
531
+ * parseDateTime('next Friday')
532
+ * // Returns a Date object for next Friday
533
+ *
534
+ * // Pass through existing Date objects
535
+ * const date = new Date('2023-06-15')
536
+ * parseDateTime(date)
537
+ * // Returns the same Date object
538
+ *
539
+ * // Parse time strings
540
+ * parseDateTime('3:30pm')
541
+ * // Returns a Date object for today at 3:30 PM
542
+ *
543
+ * // Parse complex date expressions
544
+ * parseDateTime('3 days after next Monday')
545
+ * // Returns the appropriate Date object
546
+ * ```
547
+ */
548
+ const parseDateTime = (str) => {
549
+ if (str instanceof Date) return str;
550
+ return chrono.parseDate(str);
551
+ };
552
+ //#endregion
553
+ export { decodeJWT, generateJWT, generateRandomString, generateSecurePassword, getJWTTimeRemaining, hashPassword, isJWTExpired, jwt, needsRehash, parseDateTime, verifyJWT, verifyPassword };
554
+
555
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","names":[],"sources":["../src/functions/crypto/generate-random-string.ts","../src/functions/crypto/jwt.ts","../src/functions/crypto/password.ts","../src/functions/datetime/parse-datetime.ts"],"sourcesContent":["/**\n * Utility function for generating cryptographically secure random strings\n * Provides secure random string generation for tokens, IDs, and other security-sensitive uses\n */\n\nimport { randomBytes } from \"node:crypto\";\n\n/**\n * Generates a cryptographically secure random string of specified length\n * Uses Node.js crypto module for secure random number generation\n *\n * @param length - The length of the random string to generate\n * @returns A random string consisting of uppercase letters and numbers\n *\n * @example\n * ```ts\n * import { generateRandomString } from '@/utils/functions';\n *\n * // Generate a 6-character random string\n * const verificationCode = generateRandomString(6);\n * // Example output: \"A7B3C9\"\n *\n * // Generate a longer random string for tokens\n * const apiToken = generateRandomString(32);\n * // Example output: \"X7F9A2B5C8D1E4F7G0H3I6J9K2L5M8N1\"\n *\n * // Use as unique identifiers\n * const uniqueId = generateRandomString(12);\n * // Example output: \"B7C9D1E3F5G7\"\n * ```\n */\nexport function generateRandomString(length: number): string {\n const charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\n const randomBytesArray = randomBytes(length);\n let result = \"\";\n\n for (let i = 0; i < length; i++) {\n const randomIndex = randomBytesArray[i] % charset.length;\n result += charset[randomIndex];\n }\n\n return result;\n}\n","/**\n * JWT (JSON Web Token) utilities for SaaS applications\n * Provides secure token generation, verification, and management\n * Includes validation and error handling for production use\n */\n\nimport jwt from \"jsonwebtoken\";\n\nimport { logger } from \"../logging/logger\";\n\nexport { jwt };\n\n/**\n * Interface for JWT payload data\n */\ninterface JWTPayload {\n /** User ID */\n userId: string;\n /** User email */\n email: string;\n /** User role */\n role?: string;\n /** Workspace ID */\n workspaceId?: string;\n /** Token expiration time (Unix timestamp) */\n exp?: number;\n /** Token issued at time (Unix timestamp) */\n iat?: number;\n /** Additional custom claims */\n [key: string]: unknown;\n /** For Supabase */\n aud?: string;\n sub?: string;\n}\n\n/**\n * Interface for JWT options\n */\ninterface JWTOptions {\n /** Token expiration time (default: '7d') */\n expiresIn?: string;\n /** Token issuer */\n issuer?: string;\n /** Token audience */\n audience?: string;\n}\n\n/**\n * Interface for JWT verification result\n */\ninterface JWTVerificationResult {\n /** Whether the token is valid */\n isValid: boolean;\n /** Decoded payload if valid */\n payload?: JWTPayload;\n /** Error message if verification failed */\n error?: string;\n}\n\n/**\n * Generates a JWT token with the provided payload\n *\n * @param payload - The data to encode in the token\n * @param secret - The secret key for signing the token\n * @param options - Token generation options\n * @returns The generated JWT token or null if failed\n *\n * @example\n * ```typescript\n * import { generateJWT } from '@kumix/utils';\n *\n * // Basic usage\n * const token = generateJWT(\n * { userId: '123', email: 'user@example.com' },\n * process.env.JWT_SECRET\n * );\n *\n * // With custom expiration\n * const token = generateJWT(\n * { userId: '123', email: 'user@example.com', role: 'admin' },\n * process.env.JWT_SECRET,\n * { expiresIn: '1h' }\n * );\n *\n * // For API authentication\n * const authToken = generateJWT(\n * { userId: user.id, email: user.email, workspaceId: workspace.id },\n * process.env.JWT_SECRET,\n * { expiresIn: '24h', issuer: 'myapp.com' }\n * );\n * ```\n */\nexport function generateJWT(\n payload: JWTPayload,\n secret: string,\n options: JWTOptions = {},\n): string | null {\n // Validate inputs\n if (!payload || typeof payload !== \"object\") {\n logger.warn(\"JWT generation: Payload must be a valid object\");\n return null;\n }\n\n if (!payload.userId || typeof payload.userId !== \"string\") {\n logger.warn(\"JWT generation: userId is required in payload\");\n return null;\n }\n\n if (!payload.email || typeof payload.email !== \"string\") {\n logger.warn(\"JWT generation: email is required in payload\");\n return null;\n }\n\n if (!secret || typeof secret !== \"string\") {\n logger.warn(\"JWT generation: Secret must be a non-empty string\");\n return null;\n }\n\n const { expiresIn = \"7d\", issuer, audience } = options;\n\n const { exp, ...cleanPayload } = payload;\n\n try {\n const signOptions = {\n ...(typeof exp === \"undefined\" ? { expiresIn } : {}),\n ...(issuer && { issuer }),\n ...(audience && { audience }),\n } as jwt.SignOptions;\n\n const token = jwt.sign(cleanPayload, secret, signOptions);\n\n return token;\n } catch (error) {\n logger.error(\"JWT generation: Failed to generate token\", { error });\n return null;\n }\n}\n\n/**\n * Verifies and decodes a JWT token\n *\n * @param token - The JWT token to verify\n * @param secret - The secret key used to sign the token\n * @param options - Verification options\n * @returns Verification result with payload if valid\n *\n * @example\n * ```typescript\n * import { verifyJWT } from '@kumix/utils';\n *\n * // Basic usage\n * const result = verifyJWT(token, process.env.JWT_SECRET);\n * if (result.isValid && result.payload) {\n * const { userId, email } = result.payload;\n * // Proceed with authenticated user\n * }\n *\n * // With issuer verification\n * const result = verifyJWT(token, process.env.JWT_SECRET, {\n * issuer: 'myapp.com'\n * });\n *\n * // In authentication middleware\n * async function authenticateRequest(req, res, next) {\n * const token = req.headers.authorization?.replace('Bearer ', '');\n * const result = verifyJWT(token, process.env.JWT_SECRET);\n *\n * if (!result.isValid) {\n * return res.status(401).json({ error: 'Invalid token' });\n * }\n *\n * req.user = result.payload;\n * next();\n * }\n * ```\n */\nexport function verifyJWT(\n token: string,\n secret: string,\n options: { issuer?: string; audience?: string } = {},\n): JWTVerificationResult {\n // Validate inputs\n if (!token || typeof token !== \"string\") {\n return {\n isValid: false,\n error: \"Token must be a non-empty string\",\n };\n }\n\n if (!secret || typeof secret !== \"string\") {\n return {\n isValid: false,\n error: \"Secret must be a non-empty string\",\n };\n }\n\n const { issuer, audience } = options;\n\n try {\n const decoded = jwt.verify(token, secret, {\n ...(issuer && { issuer }),\n ...(audience && { audience }),\n }) as JWTPayload;\n\n return {\n isValid: true,\n payload: decoded,\n };\n } catch (error) {\n logger.warn(\"JWT verification: Token verification failed\", { error });\n\n if (error instanceof jwt.TokenExpiredError) {\n return {\n isValid: false,\n error: \"Token has expired\",\n };\n }\n\n if (error instanceof jwt.JsonWebTokenError) {\n return {\n isValid: false,\n error: \"Invalid token format\",\n };\n }\n\n return {\n isValid: false,\n error: \"Token verification failed\",\n };\n }\n}\n\n/**\n * Decodes a JWT token without verification (for debugging/inspection)\n *\n * @param token - The JWT token to decode\n * @returns Decoded payload or null if failed\n *\n * @example\n * ```typescript\n * import { decodeJWT } from '@kumix/utils';\n *\n * // Inspect token contents without verification\n * const payload = decodeJWT(token);\n * if (payload) {\n * console.log('Token expires at:', new Date(payload.exp * 1000));\n * console.log('User ID:', payload.userId);\n * }\n *\n * // For debugging purposes\n * function debugToken(token: string) {\n * const payload = decodeJWT(token);\n * if (payload) {\n * console.log('Token payload:', payload);\n * } else {\n * console.log('Invalid token format');\n * }\n * }\n * ```\n */\nexport function decodeJWT(token: string): JWTPayload | null {\n if (!token || typeof token !== \"string\") {\n return null;\n }\n\n try {\n const decoded = jwt.decode(token) as JWTPayload;\n return decoded;\n } catch (error) {\n logger.warn(\"JWT decode: Failed to decode token\", { error });\n return null;\n }\n}\n\n/**\n * Checks if a JWT token is expired\n *\n * @param token - The JWT token to check\n * @returns True if token is expired, false otherwise\n *\n * @example\n * ```typescript\n * import { isJWTExpired } from '@kumix/utils';\n *\n * // Check if token needs refresh\n * if (isJWTExpired(userToken)) {\n * // Redirect to login or refresh token\n * await refreshUserToken();\n * }\n *\n * // In token refresh logic\n * function shouldRefreshToken(token: string): boolean {\n * return isJWTExpired(token);\n * }\n * ```\n */\nexport function isJWTExpired(token: string): boolean {\n const payload = decodeJWT(token);\n if (!payload?.exp) {\n return true; // Consider invalid tokens as expired\n }\n\n const currentTime = Math.floor(Date.now() / 1000);\n return payload.exp < currentTime;\n}\n\n/**\n * Gets the remaining time until token expiration\n *\n * @param token - The JWT token to check\n * @returns Remaining time in seconds, or 0 if expired/invalid\n *\n * @example\n * ```typescript\n * import { getJWTTimeRemaining } from '@kumix/utils';\n *\n * // Check how much time is left\n * const timeLeft = getJWTTimeRemaining(userToken);\n * if (timeLeft < 300) { // Less than 5 minutes\n * // Show warning about token expiration\n * showTokenExpirationWarning();\n * }\n *\n * // Format time remaining for display\n * function formatTimeRemaining(token: string): string {\n * const seconds = getJWTTimeRemaining(token);\n * const minutes = Math.floor(seconds / 60);\n * const hours = Math.floor(minutes / 60);\n *\n * if (hours > 0) return `${hours}h ${minutes % 60}m`;\n * if (minutes > 0) return `${minutes}m`;\n * return `${seconds}s`;\n * }\n * ```\n */\nexport function getJWTTimeRemaining(token: string): number {\n const payload = decodeJWT(token);\n if (!payload?.exp) {\n return 0;\n }\n\n const currentTime = Math.floor(Date.now() / 1000);\n const timeRemaining = payload.exp - currentTime;\n\n return Math.max(0, timeRemaining);\n}\n","/**\n * Password hashing and verification utilities for SaaS applications\n * Provides secure password handling using bcrypt with proper salt rounds\n * Includes validation and error handling for production use\n */\n\nimport { randomInt } from \"node:crypto\";\n\nimport bcrypt from \"bcryptjs\";\n\nimport { logger } from \"../logging/logger\";\n\n/**\n * Default salt rounds for bcrypt hashing\n * 12 rounds provides good security while maintaining reasonable performance\n * Higher values increase security but also increase computation time\n */\nconst DEFAULT_SALT_ROUNDS = 12;\n\n/**\n * Interface for password hashing options\n */\ninterface HashPasswordOptions {\n /** Number of salt rounds for bcrypt (default: 12) */\n saltRounds?: number;\n}\n\n/**\n * Interface for password verification result\n */\ninterface VerifyPasswordResult {\n /** Whether the password is valid */\n isValid: boolean;\n /** Error message if verification failed */\n error?: string;\n}\n\n/**\n * Hashes a password using bcrypt with salt\n *\n * @param password - The plain text password to hash\n * @param options - Hashing options\n * @returns Promise that resolves to the hashed password or null if failed\n *\n * @example\n * ```typescript\n * import { hashPassword } from '@kumix/utils';\n *\n * // Basic usage\n * const hashedPassword = await hashPassword('userPassword123');\n * if (hashedPassword) {\n * // Store hashedPassword in database\n * await saveUser({ email, password: hashedPassword });\n * }\n *\n * // With custom salt rounds\n * const hashedPassword = await hashPassword('userPassword123', {\n * saltRounds: 14\n * });\n *\n * // Error handling\n * const hashedPassword = await hashPassword('userPassword123');\n * if (!hashedPassword) {\n * throw new Error('Failed to hash password');\n * }\n * ```\n */\nexport async function hashPassword(\n password: string,\n options: HashPasswordOptions = {},\n): Promise<string | null> {\n // Validate input\n if (!password || typeof password !== \"string\") {\n logger.warn(\"Password hashing: Password must be a non-empty string\");\n return null;\n }\n\n if (password.length < 1) {\n logger.warn(\"Password hashing: Password cannot be empty\");\n return null;\n }\n\n const { saltRounds = DEFAULT_SALT_ROUNDS } = options;\n\n // Validate salt rounds\n if (typeof saltRounds !== \"number\" || saltRounds < 4 || saltRounds > 20) {\n logger.warn(\"Password hashing: Salt rounds must be between 4 and 20\");\n return null;\n }\n\n try {\n // Generate salt and hash password\n const salt = await bcrypt.genSalt(saltRounds);\n const hashedPassword = await bcrypt.hash(password, salt);\n\n return hashedPassword;\n } catch (error) {\n logger.error(\"Password hashing: Failed to hash password\", { error });\n return null;\n }\n}\n\n/**\n * Verifies a password against its hash\n *\n * @param password - The plain text password to verify\n * @param hashedPassword - The hashed password to compare against\n * @returns Promise that resolves to verification result\n *\n * @example\n * ```typescript\n * import { verifyPassword } from '@kumix/utils';\n *\n * // Basic usage\n * const result = await verifyPassword('userPassword123', storedHashedPassword);\n * if (result.isValid) {\n * // Password is correct, proceed with login\n * await loginUser(user);\n * } else {\n * // Password is incorrect\n * throw new Error('Invalid credentials');\n * }\n *\n * // With error handling\n * const result = await verifyPassword('userPassword123', storedHashedPassword);\n * if (result.error) {\n * logger.error('Password verification failed:', result.error);\n * throw new Error('Authentication error');\n * }\n *\n * // In authentication middleware\n * async function authenticateUser(email: string, password: string) {\n * const user = await getUserByEmail(email);\n * if (!user) {\n * return { success: false, error: 'User not found' };\n * }\n *\n * const result = await verifyPassword(password, user.hashedPassword);\n * if (!result.isValid) {\n * return { success: false, error: 'Invalid password' };\n * }\n *\n * return { success: true, user };\n * }\n * ```\n */\nexport async function verifyPassword(\n password: string,\n hashedPassword: string,\n): Promise<VerifyPasswordResult> {\n // Validate inputs\n if (!password || typeof password !== \"string\") {\n return {\n isValid: false,\n error: \"Password must be a non-empty string\",\n };\n }\n\n if (!hashedPassword || typeof hashedPassword !== \"string\") {\n return {\n isValid: false,\n error: \"Hashed password must be a non-empty string\",\n };\n }\n\n // Basic bcrypt hash format validation\n if (\n !hashedPassword.startsWith(\"$2a$\") &&\n !hashedPassword.startsWith(\"$2b$\") &&\n !hashedPassword.startsWith(\"$2y$\")\n ) {\n return {\n isValid: false,\n error: \"Invalid hash format\",\n };\n }\n\n try {\n const isValid = await bcrypt.compare(password, hashedPassword);\n return { isValid };\n } catch (error) {\n logger.error(\"Password verification: Failed to verify password\", { error });\n return {\n isValid: false,\n error: \"Password verification failed\",\n };\n }\n}\n\n/**\n * Checks if a password needs to be rehashed (e.g., due to updated salt rounds)\n *\n * @param hashedPassword - The current hashed password\n * @param targetSaltRounds - The target salt rounds (default: 12)\n * @returns Whether the password needs to be rehashed\n *\n * @example\n * ```typescript\n * import { needsRehash, hashPassword } from '@kumix/utils';\n *\n * // Check if password needs rehashing during login\n * async function loginUser(email: string, password: string) {\n * const user = await getUserByEmail(email);\n * const result = await verifyPassword(password, user.hashedPassword);\n *\n * if (result.isValid) {\n * // Check if we need to rehash with updated salt rounds\n * if (needsRehash(user.hashedPassword, 14)) {\n * const newHash = await hashPassword(password, { saltRounds: 14 });\n * if (newHash) {\n * await updateUserPassword(user.id, newHash);\n * }\n * }\n *\n * return { success: true, user };\n * }\n *\n * return { success: false, error: 'Invalid credentials' };\n * }\n * ```\n */\nexport function needsRehash(\n hashedPassword: string,\n targetSaltRounds: number = DEFAULT_SALT_ROUNDS,\n): boolean {\n if (!hashedPassword || typeof hashedPassword !== \"string\") {\n return true; // Invalid hash should be rehashed\n }\n\n try {\n // Extract salt rounds from hash\n const parts = hashedPassword.split(\"$\");\n if (parts.length < 4) {\n return true; // Invalid format\n }\n\n const currentSaltRounds = parseInt(parts[2], 10);\n return currentSaltRounds !== targetSaltRounds;\n } catch (error) {\n logger.warn(\"Password rehash check: Failed to parse hash\", { error });\n return true; // If we can't parse, assume it needs rehashing\n }\n}\n\n/**\n * Generates a secure random password\n *\n * @param length - Length of the password (default: 16, min: 8, max: 128)\n * @param options - Password generation options\n * @returns Generated password string\n *\n * @example\n * ```typescript\n * import { generateSecurePassword } from '@kumix/utils';\n *\n * // Generate default password (16 characters)\n * const password = generateSecurePassword();\n *\n * // Generate longer password\n * const longPassword = generateSecurePassword(24);\n *\n * // Generate password without symbols\n * const simplePassword = generateSecurePassword(12, {\n * includeSymbols: false\n * });\n *\n * // For temporary passwords\n * const tempPassword = generateSecurePassword(12);\n * await sendPasswordResetEmail(user.email, tempPassword);\n * ```\n */\nexport function generateSecurePassword(\n length: number = 16,\n options: {\n includeUppercase?: boolean;\n includeLowercase?: boolean;\n includeNumbers?: boolean;\n includeSymbols?: boolean;\n } = {},\n): string {\n const {\n includeUppercase = true,\n includeLowercase = true,\n includeNumbers = true,\n includeSymbols = true,\n } = options;\n\n // Validate length\n const validLength = Math.max(8, Math.min(128, length));\n\n let charset = \"\";\n if (includeUppercase) charset += \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n if (includeLowercase) charset += \"abcdefghijklmnopqrstuvwxyz\";\n if (includeNumbers) charset += \"0123456789\";\n if (includeSymbols) charset += \"!@#$%^&*()_+-=[]{}|;:,.<>?\";\n\n if (charset.length === 0) {\n // Fallback to alphanumeric if no character sets selected\n charset = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n }\n\n let password = \"\";\n for (let i = 0; i < validLength; i++) {\n // Use a cryptographically secure RNG to avoid bias and predictability\n const randomIndex = randomInt(0, charset.length);\n password += charset[randomIndex];\n }\n\n return password;\n}\n","/**\n * Utility function for parsing date strings into Date objects\n * Provides natural language date parsing capabilities\n */\n\nimport * as chrono from \"chrono-node\";\n\n/**\n * Parses a date string into a Date object using natural language processing\n * Passes through Date objects unchanged\n *\n * @param str - The date string to parse or a Date object to pass through\n * @returns A Date object, or null if parsing fails\n *\n * @example\n * ```ts\n * // Parse natural language date strings\n * parseDateTime('tomorrow at 3pm')\n * // Returns a Date object for tomorrow at 3:00 PM\n *\n * // Parse formal date strings\n * parseDateTime('2023-06-15')\n * // Returns a Date object for June 15, 2023\n *\n * // Parse relative dates\n * parseDateTime('next Friday')\n * // Returns a Date object for next Friday\n *\n * // Pass through existing Date objects\n * const date = new Date('2023-06-15')\n * parseDateTime(date)\n * // Returns the same Date object\n *\n * // Parse time strings\n * parseDateTime('3:30pm')\n * // Returns a Date object for today at 3:30 PM\n *\n * // Parse complex date expressions\n * parseDateTime('3 days after next Monday')\n * // Returns the appropriate Date object\n * ```\n */\nexport const parseDateTime = (str: Date | string): Date | null => {\n if (str instanceof Date) return str;\n return chrono.parseDate(str);\n};\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@kumix/utils",
3
+ "version": "0.1.0",
4
+ "description": "A comprehensive library of utility functions for building modern SaaS applications.",
5
+ "author": "Kumix Labs <hai@kumix.io>",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "sideEffects": false,
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./server": {
18
+ "types": "./dist/server.d.ts",
19
+ "import": "./dist/server.js"
20
+ }
21
+ },
22
+ "scripts": {
23
+ "clean": "rimraf dist",
24
+ "clean:all": "rimraf .turbo coverage dist node_modules",
25
+ "dev": "tsdown --watch",
26
+ "build": "tsdown",
27
+ "types:check": "tsc --noEmit",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "test:coverage": "vitest run --coverage"
31
+ },
32
+ "homepage": "https://github.com/kumixlabs/toolkits",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/kumixlabs/toolkits.git",
36
+ "directory": "packages/utils"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/kumixlabs/toolkits/issues"
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ],
44
+ "keywords": [
45
+ "kumix",
46
+ "utils"
47
+ ],
48
+ "dependencies": {
49
+ "@paralleldrive/cuid2": "^3.3.0",
50
+ "@sindresorhus/slugify": "^3.0.0",
51
+ "base-x": "^5.0.1",
52
+ "bcryptjs": "^3.0.3",
53
+ "chrono-node": "2.9.1",
54
+ "clsx": "^2.1.1",
55
+ "consola": "^3.4.2",
56
+ "jsonwebtoken": "^9.0.3",
57
+ "ms": "^2.1.3",
58
+ "nanoid": "^5.1.16",
59
+ "tailwind-merge": "^3.6.0"
60
+ },
61
+ "devDependencies": {
62
+ "@kumix/tsconfig": "^0.1.0",
63
+ "@types/jsonwebtoken": "^9.0.10",
64
+ "@types/ms": "^2.1.0",
65
+ "@types/node": "^26.1.0",
66
+ "tsdown": "^0.22.3",
67
+ "typescript": "^6.0.3"
68
+ },
69
+ "publishConfig": {
70
+ "registry": "https://registry.npmjs.org/",
71
+ "access": "public"
72
+ }
73
+ }