@splendidlabz/utils 1.5.1 → 1.6.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/CHANGELOG.md +6 -0
- package/dist/dom/cookie.d.ts +2 -8
- package/dist/dom/random-string.d.ts +1 -1
- package/dist/lib/checks.d.ts +1 -0
- package/dist/lib/hash.d.ts +1 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/objects/equal.d.ts +1 -1
- package/dist/lib/objects/split.d.ts +2 -0
- package/dist/lib/strings/index.d.ts +1 -0
- package/dist/lib/strings/name.d.ts +5 -0
- package/dist/node/random-string.d.ts +1 -2
- package/dom/cookie.js +8 -7
- package/dom/random-string.js +1 -1
- package/dom/random-string.test.js +13 -0
- package/lib/checks.js +16 -0
- package/lib/hash.js +18 -0
- package/lib/hash.spec.js +163 -0
- package/lib/index.js +1 -0
- package/lib/objects/mix/mix.test.js +8 -0
- package/lib/objects/split.js +1 -1
- package/lib/strings/index.js +1 -0
- package/lib/strings/name.js +24 -0
- package/lib/strings/name.test.js +67 -0
- package/node/random-string.js +6 -6
- package/node/random-string.test.js +15 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
package/dist/dom/cookie.d.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
* Gets a cookie by name
|
|
3
|
-
* Thanks to https://stackoverflow.com/a/21125098
|
|
4
|
-
* @param {string} name Cookie name
|
|
5
|
-
* @returns string Cookie value
|
|
6
|
-
*/
|
|
7
|
-
export function getCookie(name: string): string;
|
|
1
|
+
export function getCookie(name: any): any;
|
|
8
2
|
export namespace cookies {
|
|
9
|
-
function get(name: any):
|
|
3
|
+
function get(name: any): any;
|
|
10
4
|
function set(name: any, value: any, days: any): void;
|
|
11
5
|
}
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* Ensures first character is a letter so it can be used in HTML ids and classes
|
|
4
4
|
* This can actually be used everywhere, not just in the browser.
|
|
5
5
|
*/
|
|
6
|
-
export function randomString(length
|
|
6
|
+
export function randomString(length?: number): string;
|
|
7
7
|
export function uuid(): `${string}-${string}-${string}-${string}-${string}`;
|
package/dist/lib/checks.d.ts
CHANGED
|
@@ -27,3 +27,4 @@ export function isArray(x: unknown): boolean;
|
|
|
27
27
|
* notObject({}) // false
|
|
28
28
|
*/
|
|
29
29
|
export function notObject(x: unknown): boolean;
|
|
30
|
+
export function getType(x: any): "object" | "string" | "integer" | "float" | "boolean" | "function" | "undefined" | "symbol" | "bigint" | "array" | "unknown";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function isHashedValue(value: any, type?: string): boolean;
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./checks.js";
|
|
|
4
4
|
export * from "./date/index.js";
|
|
5
5
|
export * from "./form/index.js";
|
|
6
6
|
export * from "./functions/index.js";
|
|
7
|
+
export * from "./hash.js";
|
|
7
8
|
export * from "./numbers/index.js";
|
|
8
9
|
export * from "./objects/index.js";
|
|
9
10
|
export * from "./promises/index.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export function isEqual(a: any, b: any):
|
|
1
|
+
export function isEqual(a: any, b: any): boolean;
|
|
2
2
|
export function notEqual(a: any, b: any): boolean;
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export function randomString(length?: number):
|
|
2
|
-
export function randomStringSync(length?: number): string;
|
|
1
|
+
export function randomString(length?: number): string;
|
package/dom/cookie.js
CHANGED
|
@@ -8,15 +8,16 @@ export const cookies = {
|
|
|
8
8
|
},
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* Gets a cookie by name
|
|
13
|
-
* Thanks to https://stackoverflow.com/a/21125098
|
|
14
|
-
* @param {string} name Cookie name
|
|
15
|
-
* @returns string Cookie value
|
|
16
|
-
*/
|
|
17
11
|
export function getCookie(name) {
|
|
18
12
|
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'))
|
|
19
|
-
if (match) return
|
|
13
|
+
if (!match) return null
|
|
14
|
+
const decoded = decodeURIComponent(match[2])
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(decoded)
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return decoded
|
|
20
|
+
}
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
// By Copilot. Needs testing before confirmation.
|
package/dom/random-string.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Ensures first character is a letter so it can be used in HTML ids and classes
|
|
4
4
|
* This can actually be used everywhere, not just in the browser.
|
|
5
5
|
*/
|
|
6
|
-
export function randomString(length) {
|
|
6
|
+
export function randomString(length = 10) {
|
|
7
7
|
const firstLetter = String.fromCharCode(65 + Math.floor(Math.random() * 26))
|
|
8
8
|
const others = Math.random()
|
|
9
9
|
.toString(36)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { randomString } from './random-string.js'
|
|
3
|
+
|
|
4
|
+
describe('randomString', () => {
|
|
5
|
+
it('should generate a random string', () => {
|
|
6
|
+
const str = randomString()
|
|
7
|
+
expect(str).toBeDefined()
|
|
8
|
+
expect(str.length).toBe(10)
|
|
9
|
+
|
|
10
|
+
// Ensure the first character is a letter
|
|
11
|
+
expect(str[0]).toMatch(/[a-zA-Z]/)
|
|
12
|
+
})
|
|
13
|
+
})
|
package/lib/checks.js
CHANGED
|
@@ -35,3 +35,19 @@ export function isArray(x) {
|
|
|
35
35
|
export function notObject(x) {
|
|
36
36
|
return !isObject(x)
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
export function getType(x) {
|
|
40
|
+
if (typeof x === 'string') return 'string'
|
|
41
|
+
if (typeof x === 'number') {
|
|
42
|
+
if (Number.isInteger(x)) return 'integer'
|
|
43
|
+
return 'float'
|
|
44
|
+
}
|
|
45
|
+
if (typeof x === 'boolean') return 'boolean'
|
|
46
|
+
if (typeof x === 'function') return 'function'
|
|
47
|
+
if (typeof x === 'undefined') return 'undefined'
|
|
48
|
+
if (typeof x === 'symbol') return 'symbol'
|
|
49
|
+
if (typeof x === 'bigint') return 'bigint'
|
|
50
|
+
if (Array.isArray(x)) return 'array'
|
|
51
|
+
if (typeof x === 'object') return 'object'
|
|
52
|
+
return 'unknown'
|
|
53
|
+
}
|
package/lib/hash.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function isHashedValue(value, type = 'sha256') {
|
|
2
|
+
if (typeof value !== 'string') return null
|
|
3
|
+
value = value.trim()
|
|
4
|
+
type = type.toLowerCase()
|
|
5
|
+
|
|
6
|
+
if (type === 'sha256') {
|
|
7
|
+
return value.length === 64 && /^[a-fA-F0-9]{64}$/.test(value)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (type === 'sha1') {
|
|
11
|
+
return value.length === 40 && /^[a-fA-F0-9]{40}$/.test(value)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (type === 'md5') {
|
|
15
|
+
return value.length === 32 && /^[a-fA-F0-9]{32}$/.test(value)
|
|
16
|
+
}
|
|
17
|
+
return null
|
|
18
|
+
}
|
package/lib/hash.spec.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { isHashedValue } from './hash.js'
|
|
3
|
+
|
|
4
|
+
describe('isHashedValue', () => {
|
|
5
|
+
describe('SHA256 hashes', () => {
|
|
6
|
+
it('returns true for valid SHA256 hash', () => {
|
|
7
|
+
const validSha256 =
|
|
8
|
+
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3'
|
|
9
|
+
expect(isHashedValue(validSha256, 'sha256')).toBe(true)
|
|
10
|
+
expect(isHashedValue(validSha256)).toBe(true) // default type
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('returns true for valid SHA256 hash with uppercase letters', () => {
|
|
14
|
+
const validSha256 =
|
|
15
|
+
'A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3'
|
|
16
|
+
expect(isHashedValue(validSha256, 'sha256')).toBe(true)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('returns true for valid SHA256 hash with mixed case', () => {
|
|
20
|
+
const validSha256 =
|
|
21
|
+
'A665a45920422F9d417E4867efdc4FB8a04a1F3fff1FA07e998E86f7f7A27ae3'
|
|
22
|
+
expect(isHashedValue(validSha256, 'sha256')).toBe(true)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('returns false for SHA256 hash with wrong length', () => {
|
|
26
|
+
const shortHash =
|
|
27
|
+
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae'
|
|
28
|
+
const longHash =
|
|
29
|
+
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae33'
|
|
30
|
+
expect(isHashedValue(shortHash, 'sha256')).toBe(false)
|
|
31
|
+
expect(isHashedValue(longHash, 'sha256')).toBe(false)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('returns false for SHA256 hash with invalid characters', () => {
|
|
35
|
+
const invalidHash =
|
|
36
|
+
'g665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3'
|
|
37
|
+
expect(isHashedValue(invalidHash, 'sha256')).toBe(false)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe('SHA1 hashes', () => {
|
|
42
|
+
it('returns true for valid SHA1 hash', () => {
|
|
43
|
+
const validSha1 = '356a192b7913b04c54574d18c28d46e6395428ab'
|
|
44
|
+
expect(isHashedValue(validSha1, 'sha1')).toBe(true)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('returns true for valid SHA1 hash with uppercase letters', () => {
|
|
48
|
+
const validSha1 = '356A192B7913B04C54574D18C28D46E6395428AB'
|
|
49
|
+
expect(isHashedValue(validSha1, 'sha1')).toBe(true)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('returns false for SHA1 hash with wrong length', () => {
|
|
53
|
+
const shortHash = '356a192b7913b04c54574d18c28d46e6395428a'
|
|
54
|
+
const longHash = '356a192b7913b04c54574d18c28d46e6395428abb'
|
|
55
|
+
expect(isHashedValue(shortHash, 'sha1')).toBe(false)
|
|
56
|
+
expect(isHashedValue(longHash, 'sha1')).toBe(false)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('returns false for SHA1 hash with invalid characters', () => {
|
|
60
|
+
const invalidHash = '356g192b7913b04c54574d18c28d46e6395428ab'
|
|
61
|
+
expect(isHashedValue(invalidHash, 'sha1')).toBe(false)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe('MD5 hashes', () => {
|
|
66
|
+
it('returns true for valid MD5 hash', () => {
|
|
67
|
+
const validMd5 = '5d41402abc4b2a76b9719d911017c592'
|
|
68
|
+
expect(isHashedValue(validMd5, 'md5')).toBe(true)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('returns true for valid MD5 hash with uppercase letters', () => {
|
|
72
|
+
const validMd5 = '5D41402ABC4B2A76B9719D911017C592'
|
|
73
|
+
expect(isHashedValue(validMd5, 'md5')).toBe(true)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('returns false for MD5 hash with wrong length', () => {
|
|
77
|
+
const shortHash = '5d41402abc4b2a76b9719d911017c59'
|
|
78
|
+
const longHash = '5d41402abc4b2a76b9719d911017c5922'
|
|
79
|
+
expect(isHashedValue(shortHash, 'md5')).toBe(false)
|
|
80
|
+
expect(isHashedValue(longHash, 'md5')).toBe(false)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('returns false for MD5 hash with invalid characters', () => {
|
|
84
|
+
const invalidHash = '5g41402abc4b2a76b9719d911017c592'
|
|
85
|
+
expect(isHashedValue(invalidHash, 'md5')).toBe(false)
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
describe('Type parameter handling', () => {
|
|
90
|
+
it('handles case insensitive type parameter', () => {
|
|
91
|
+
const validSha256 =
|
|
92
|
+
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3'
|
|
93
|
+
expect(isHashedValue(validSha256, 'SHA256')).toBe(true)
|
|
94
|
+
expect(isHashedValue(validSha256, 'Sha256')).toBe(true)
|
|
95
|
+
expect(isHashedValue(validSha256, 'sHa256')).toBe(true)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('returns null for unsupported hash type', () => {
|
|
99
|
+
const validSha256 =
|
|
100
|
+
'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3'
|
|
101
|
+
expect(isHashedValue(validSha256, 'sha512')).toBe(null)
|
|
102
|
+
expect(isHashedValue(validSha256, 'blake2b')).toBe(null)
|
|
103
|
+
expect(isHashedValue(validSha256, 'unknown')).toBe(null)
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
describe('Input validation', () => {
|
|
108
|
+
it('returns null for non-string inputs', () => {
|
|
109
|
+
expect(isHashedValue(null)).toBe(null)
|
|
110
|
+
expect(isHashedValue(undefined)).toBe(null)
|
|
111
|
+
expect(isHashedValue(123)).toBe(null)
|
|
112
|
+
expect(isHashedValue([])).toBe(null)
|
|
113
|
+
expect(isHashedValue({})).toBe(null)
|
|
114
|
+
expect(isHashedValue(true)).toBe(null)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('handles empty string', () => {
|
|
118
|
+
expect(isHashedValue('')).toBe(false)
|
|
119
|
+
expect(isHashedValue('', 'sha1')).toBe(false)
|
|
120
|
+
expect(isHashedValue('', 'md5')).toBe(false)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('trims whitespace from input', () => {
|
|
124
|
+
const validSha256 =
|
|
125
|
+
' a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3 '
|
|
126
|
+
expect(isHashedValue(validSha256, 'sha256')).toBe(true)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('returns false for strings with only whitespace', () => {
|
|
130
|
+
expect(isHashedValue(' ')).toBe(false)
|
|
131
|
+
expect(isHashedValue('\t\n')).toBe(false)
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
describe('Edge cases', () => {
|
|
136
|
+
it('returns false for strings that are all zeros but correct length', () => {
|
|
137
|
+
const zeroSha256 =
|
|
138
|
+
'0000000000000000000000000000000000000000000000000000000000000000'
|
|
139
|
+
const zeroSha1 = '0000000000000000000000000000000000000000'
|
|
140
|
+
const zeroMd5 = '00000000000000000000000000000000'
|
|
141
|
+
|
|
142
|
+
expect(isHashedValue(zeroSha256, 'sha256')).toBe(true)
|
|
143
|
+
expect(isHashedValue(zeroSha1, 'sha1')).toBe(true)
|
|
144
|
+
expect(isHashedValue(zeroMd5, 'md5')).toBe(true)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('returns false for correct length but non-hex characters', () => {
|
|
148
|
+
const nonHexSha256 =
|
|
149
|
+
'g665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3'
|
|
150
|
+
const nonHexSha1 = '356g192b7913b04c54574d18c28d46e6395428ab'
|
|
151
|
+
const nonHexMd5 = '5g41402abc4b2a76b9719d911017c592'
|
|
152
|
+
|
|
153
|
+
expect(isHashedValue(nonHexSha256, 'sha256')).toBe(false)
|
|
154
|
+
expect(isHashedValue(nonHexSha1, 'sha1')).toBe(false)
|
|
155
|
+
expect(isHashedValue(nonHexMd5, 'md5')).toBe(false)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('handles special characters correctly', () => {
|
|
159
|
+
const specialChars = '!@#$%^&*()_+-=[]{}|;:,.<>?/~`'
|
|
160
|
+
expect(isHashedValue(specialChars, 'sha256')).toBe(false)
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
})
|
package/lib/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export * from './checks.js'
|
|
|
4
4
|
export * from './date/index.js'
|
|
5
5
|
export * from './form/index.js'
|
|
6
6
|
export * from './functions/index.js'
|
|
7
|
+
export * from './hash.js'
|
|
7
8
|
export * from './numbers/index.js'
|
|
8
9
|
export * from './objects/index.js'
|
|
9
10
|
export * from './promises/index.js'
|
|
@@ -338,3 +338,11 @@ it('should skip null and undefined sources', () => {
|
|
|
338
338
|
|
|
339
339
|
expect(mix(null, obj1, undefined, obj2, null)).toEqual(expected)
|
|
340
340
|
})
|
|
341
|
+
|
|
342
|
+
it('should not skip null and false properties', () => {
|
|
343
|
+
const obj1 = { a: 1, b: 2, c: 3 }
|
|
344
|
+
const obj2 = { a: null, b: false, c: undefined }
|
|
345
|
+
const expected = { a: null, b: false, c: undefined }
|
|
346
|
+
|
|
347
|
+
expect(mix(obj1, obj2)).toEqual(expected)
|
|
348
|
+
})
|
package/lib/objects/split.js
CHANGED
package/lib/strings/index.js
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function parseFullName(name) {
|
|
2
|
+
const parts = name.trim().split(/\s+/)
|
|
3
|
+
|
|
4
|
+
if (parts.length === 1) {
|
|
5
|
+
return { firstName: parts[0], middleName: '', lastName: '' }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (parts.length === 2) {
|
|
9
|
+
return { firstName: parts[0], middleName: '', lastName: parts[1] }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (parts.length === 3) {
|
|
13
|
+
const firstName = parts.slice(0, 2).join(' ')
|
|
14
|
+
const lastName = parts[2]
|
|
15
|
+
return { firstName, middleName: '', lastName }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 4+ words: first 2 words = firstName, last word = lastName, middle = middleName
|
|
19
|
+
const firstName = parts.slice(0, 2).join(' ')
|
|
20
|
+
const lastName = parts[parts.length - 1]
|
|
21
|
+
const middleName = parts.slice(2, -1).join(' ')
|
|
22
|
+
|
|
23
|
+
return { firstName, middleName, lastName }
|
|
24
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { parseFullName } from './name.js'
|
|
3
|
+
|
|
4
|
+
describe('parseFullName', () => {
|
|
5
|
+
test('handles single name', () => {
|
|
6
|
+
const result = parseFullName('John')
|
|
7
|
+
expect(result).toEqual({
|
|
8
|
+
firstName: 'John',
|
|
9
|
+
middleName: '',
|
|
10
|
+
lastName: '',
|
|
11
|
+
})
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('handles two names', () => {
|
|
15
|
+
const result = parseFullName('John Doe')
|
|
16
|
+
expect(result).toEqual({
|
|
17
|
+
firstName: 'John',
|
|
18
|
+
middleName: '',
|
|
19
|
+
lastName: 'Doe',
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('handles three names (Chinese pattern)', () => {
|
|
24
|
+
const result = parseFullName('Wee Keat Liew')
|
|
25
|
+
expect(result).toEqual({
|
|
26
|
+
firstName: 'Wee Keat',
|
|
27
|
+
middleName: '',
|
|
28
|
+
lastName: 'Liew',
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('handles four names with middle name', () => {
|
|
33
|
+
const result = parseFullName('Mary Jane Michael Doe')
|
|
34
|
+
expect(result).toEqual({
|
|
35
|
+
firstName: 'Mary Jane',
|
|
36
|
+
middleName: 'Michael',
|
|
37
|
+
lastName: 'Doe',
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('handles five names with multiple middle names', () => {
|
|
42
|
+
const result = parseFullName('Mary Jane Catherine Elizabeth Doe')
|
|
43
|
+
expect(result).toEqual({
|
|
44
|
+
firstName: 'Mary Jane',
|
|
45
|
+
middleName: 'Catherine Elizabeth',
|
|
46
|
+
lastName: 'Doe',
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('handles extra whitespace', () => {
|
|
51
|
+
const result = parseFullName(' John Doe ')
|
|
52
|
+
expect(result).toEqual({
|
|
53
|
+
firstName: 'John',
|
|
54
|
+
middleName: '',
|
|
55
|
+
lastName: 'Doe',
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('handles multiple spaces between words', () => {
|
|
60
|
+
const result = parseFullName('Wee Keat Liew')
|
|
61
|
+
expect(result).toEqual({
|
|
62
|
+
firstName: 'Wee Keat',
|
|
63
|
+
middleName: '',
|
|
64
|
+
lastName: 'Liew',
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
})
|
package/node/random-string.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// For Node only
|
|
2
1
|
import crypto from 'node:crypto'
|
|
3
2
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
export function randomString(length = 32) {
|
|
4
|
+
// Calculate bytes needed for desired character length
|
|
5
|
+
// Base64: 4 chars per 3 bytes, so we need ceil(length * 3 / 4) bytes
|
|
6
|
+
// Add extra bytes to ensure we have enough after encoding
|
|
7
|
+
const bytesNeeded = Math.ceil((length * 3) / 4) + 3
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
return crypto.randomBytes(length).toString('hex')
|
|
9
|
+
return crypto.randomBytes(bytesNeeded).toString('base64').slice(0, length)
|
|
10
10
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { randomString } from './random-string.js'
|
|
3
|
+
|
|
4
|
+
describe('randomString', () => {
|
|
5
|
+
it('should generate a random string', () => {
|
|
6
|
+
const str = randomString(32)
|
|
7
|
+
expect(str).toBeDefined()
|
|
8
|
+
expect(str.length).toBe(32)
|
|
9
|
+
|
|
10
|
+
const str2 = randomString(40)
|
|
11
|
+
expect(str2).toBeDefined()
|
|
12
|
+
expect(str2.length).toBe(40)
|
|
13
|
+
expect(str2).not.toBe(str)
|
|
14
|
+
})
|
|
15
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splendidlabz/utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"statuses": "^2.0.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@splendidlabz/eslint-config": "2.
|
|
54
|
+
"@splendidlabz/eslint-config": "2.1.0",
|
|
55
55
|
"jsdom": "^26.0.0",
|
|
56
56
|
"np": "^10.2.0",
|
|
57
57
|
"typescript": "^5.4.2",
|