@mhmdhammoud/meritt-utils 1.6.2 → 1.6.3

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.
@@ -1,96 +0,0 @@
1
- import { Formatter } from '../lib'
2
-
3
- describe('Formatter', () => {
4
- describe('toUpperFirst method', () => {
5
- it('should format string with - and upper first letter', () => {
6
- const formattedString = Formatter.toUpperFirst('hello world')
7
- expect(formattedString).toBe('Hello-world')
8
- })
9
-
10
- it('should format string with spaces and upper first letter of every word', () => {
11
- const formattedString = Formatter.toUpperFirst('hello_world+test')
12
- expect(formattedString).toBe('Hello-world-test')
13
- })
14
-
15
- it('should throw an error for non-string input', () => {
16
- expect(() => Formatter.toUpperFirst(123)).toThrowError(
17
- 'Provide a valid string'
18
- )
19
- })
20
- })
21
-
22
- describe('camelToKebab method', () => {
23
- it('should convert camelCase to kebab-case', () => {
24
- const kebabString = Formatter.camelToKebab('camelCaseString')
25
- expect(kebabString).toBe('camel-case-string')
26
- })
27
-
28
- it('should handle spaces and underscores', () => {
29
- const kebabString = Formatter.camelToKebab('hello World_with Spaces')
30
- expect(kebabString).toBe('hello-world_with-spaces')
31
- })
32
-
33
- it('should throw an error for non-string input', () => {
34
- expect(() => Formatter.camelToKebab(123)).toThrowError(
35
- 'Invalid input. Please provide a valid string.'
36
- )
37
- })
38
- })
39
-
40
- describe('obfuscate method', () => {
41
- it('should obfuscate email address', () => {
42
- const obfuscatedEmail = Formatter.obfuscate('johndoe@email.com')
43
- expect(obfuscatedEmail).toMatch(/^jo.*@.*$/)
44
- })
45
-
46
- it('should throw an error for non-string input', () => {
47
- //@ts-ignore
48
- expect(() => Formatter.obfuscate(123)).toThrowError(
49
- 'Provide a valid string'
50
- )
51
- })
52
-
53
- it('should throw an error for an invalid email address', () => {
54
- expect(() => Formatter.obfuscate('invalid-email')).toThrowError(
55
- 'Provide a valid email address'
56
- )
57
- })
58
- })
59
-
60
- describe('toUpperTitle method', () => {
61
- it('should capitalize first letter of each word and remove non-alphanumeric characters', () => {
62
- const upperTitle = Formatter.toUpperTitle('this is34354345a---sentence')
63
- expect(upperTitle).toBe('This Is A Sentence')
64
- })
65
-
66
- it('should throw an error for non-string input', () => {
67
- expect(() => Formatter.toUpperTitle(123)).toThrowError(
68
- 'Provide a valid string'
69
- )
70
- })
71
-
72
- it('should throw an error for empty string', () => {
73
- expect(() => Formatter.toUpperTitle('')).toThrowError(
74
- 'Provide a valid string'
75
- )
76
- })
77
- })
78
-
79
- describe('slugify method', () => {
80
- it('should generate a slug from the given string', () => {
81
- const slug = Formatter.slugify('Hello World')
82
- expect(slug).toBe('hello-world')
83
- })
84
-
85
- it('should handle special characters and spaces', () => {
86
- const slug = Formatter.slugify('Special Characters % #$ & Space')
87
- expect(slug).toBe('special-characters-space')
88
- })
89
-
90
- it('should throw an error for non-string input', () => {
91
- expect(() => Formatter.slugify(123)).toThrowError(
92
- 'Provide a valid string'
93
- )
94
- })
95
- })
96
- })
@@ -1,122 +0,0 @@
1
- import Logger, { isValidLogLevel } from '../lib/logger'
2
- import { pino } from 'pino'
3
-
4
- jest.mock('pino')
5
-
6
- const LOGGER_NAME = 'logger-name'
7
- const PINO_DESTINATION = {}
8
- const PINO = {
9
- info: jest.fn(),
10
- }
11
-
12
- describe('create logger wrapper', () => {
13
- test('initially create & configure logger', () => {
14
- //@ts-ignore
15
- const mock_pino_destination = jest
16
- .spyOn(pino, 'destination')
17
- //@ts-ignore
18
- .mockReturnValue(PINO_DESTINATION)
19
- //@ts-ignore
20
- const mock_pino = pino.mockReturnValue(PINO)
21
-
22
- /** create logger */
23
- const logger = new Logger(LOGGER_NAME)
24
-
25
- expect(logger).toBeDefined()
26
- expect(logger['_name']).toBe(LOGGER_NAME)
27
- expect(mock_pino).toHaveBeenCalledTimes(1)
28
- expect(mock_pino).toHaveBeenCalledWith(
29
- {
30
- level: 'info',
31
- timestamp: expect.any(Function),
32
- },
33
- PINO_DESTINATION
34
- )
35
- expect(mock_pino_destination).toHaveBeenCalledWith({
36
- minLength: 1024,
37
- sync: true,
38
- })
39
- })
40
- test('create repeatedly - stick to pino singleton', () => {
41
- //@ts-ignore
42
- jest.spyOn(pino, 'destination').mockReturnValue(PINO_DESTINATION)
43
- //@ts-ignore
44
- const mock_pino = pino.mockReturnValue(PINO)
45
- new Logger(LOGGER_NAME)
46
- expect(mock_pino).toHaveBeenCalledTimes(1)
47
-
48
- /** create additional logger */
49
- new Logger(LOGGER_NAME)
50
- /** no new pino created */
51
- expect(mock_pino).toHaveBeenCalledTimes(1)
52
- })
53
- })
54
- describe('validate log level', () => {
55
- test('throw on invalid log level', () => {
56
- const INVALID_LOG_LEVEL = 'invalid-log-level'
57
- expect(() => {
58
- isValidLogLevel(INVALID_LOG_LEVEL)
59
- }).toThrowError(
60
- `Invalid log level "${INVALID_LOG_LEVEL}": only error, warn, info, debug, trace are valid.`
61
- )
62
- })
63
- })
64
- describe('route and format logs', () => {
65
- const LOG_EVENT = {
66
- code: 'code',
67
- msg: 'message',
68
- }
69
- const DETAILS = {
70
- key0: 'val0',
71
- key1: 'val1',
72
- }
73
- test('log info with structured context (single object in context field)', () => {
74
- //@ts-ignore
75
- jest.spyOn(pino, 'destination').mockReturnValue(PINO_DESTINATION)
76
- //@ts-ignore
77
- pino.mockReturnValue(PINO)
78
- const logger = new Logger(LOGGER_NAME)
79
-
80
- logger.info(LOG_EVENT, DETAILS)
81
-
82
- expect(PINO.info).toHaveBeenCalledWith(
83
- expect.objectContaining({
84
- component: LOGGER_NAME,
85
- code: LOG_EVENT.code,
86
- msg: LOG_EVENT.msg,
87
- context: { key0: 'val0', key1: 'val1' },
88
- })
89
- )
90
- })
91
-
92
- test('remap reserved elastic field names in context', () => {
93
- //@ts-ignore
94
- jest.spyOn(pino, 'destination').mockReturnValue(PINO_DESTINATION)
95
- //@ts-ignore
96
- pino.mockReturnValue(PINO)
97
- const logger = new Logger(LOGGER_NAME)
98
-
99
- logger.info(LOG_EVENT, {
100
- _id: 'abc123',
101
- nested: {
102
- _id: 'nested-1',
103
- _index: 'bad-index',
104
- },
105
- })
106
-
107
- // Context holds sanitized structure; reserved names remapped recursively
108
- expect(PINO.info).toHaveBeenCalledWith(
109
- expect.objectContaining({
110
- context: expect.objectContaining({
111
- mongo_id: 'abc123',
112
- nested: { mongo_id: 'nested-1', es_index: 'bad-index' },
113
- }),
114
- })
115
- )
116
- expect(PINO.info).not.toHaveBeenCalledWith(
117
- expect.objectContaining({
118
- _id: expect.anything(),
119
- })
120
- )
121
- })
122
- })
package/src/env.d.ts DELETED
@@ -1,9 +0,0 @@
1
- declare namespace NodeJS {
2
- export interface ProcessEnv {
3
- NODE_ENV: 'development' | 'production' | 'test' | 'local'
4
- ELASTICSEARCH_NODE: string
5
- ELASTICSEARCH_USERNAME: string
6
- ELASTICSEARCH_PASSWORD: string
7
- LOG_LEVEL: string
8
- }
9
- }
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './lib'
@@ -1,167 +0,0 @@
1
- /*
2
- Author : Mhmd Hammoud https://github.com/mhmdhammoud
3
- Date : 2024-01-20
4
- Description : Color adapter
5
- Version : 1.0
6
- */
7
- class Colorful {
8
- /**
9
- * @remarks
10
- * Receives a color in RGB format and returns it in HEX format
11
- * @param color - Color in RGB format
12
- * @example
13
- * ```ts
14
- * const color = 'rgb(255, 255, 255)';
15
- * const hexColor = rgbToHex(color);
16
- * console.log(hexColor) // #ffffff
17
- * ```
18
- *
19
- * @returns Color in HEX format string
20
- */
21
- rgbToHex = (color: string): string => {
22
- const componentToHex = (c: any) => {
23
- const hex = c.toString(16)
24
- return hex.length == 1 ? '0' + hex : hex
25
- }
26
-
27
- const rgbaMatch = color.match(
28
- /rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*((\d+\.)?\d+)\)/
29
- )
30
- const rgbMatch = color.match(/rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/)
31
-
32
- let redColor, greenColor, blueColor
33
-
34
- if (rgbaMatch) {
35
- redColor = parseInt(rgbaMatch[1], 10)
36
- greenColor = parseInt(rgbaMatch[2], 10)
37
- blueColor = parseInt(rgbaMatch[3], 10)
38
- } else if (rgbMatch) {
39
- redColor = parseInt(rgbMatch[1], 10)
40
- greenColor = parseInt(rgbMatch[2], 10)
41
- blueColor = parseInt(rgbMatch[3], 10)
42
- } else {
43
- // Both patterns failed to match
44
- throw new Error(
45
- `Invalid color format '${color}'. Please provide a valid RGB or RGBA color.`
46
- )
47
- }
48
-
49
- const hexColor =
50
- '#' +
51
- componentToHex(redColor) +
52
- componentToHex(greenColor) +
53
- componentToHex(blueColor)
54
-
55
- return hexColor
56
- }
57
-
58
- /**
59
- * @remarks
60
- * Receives a color in HEX format and returns it in RGB format
61
- * @param hexColor - Color in HEX format
62
- * @example
63
- * ```ts
64
- * const hexColor = '#ffffff';
65
- * const rgbColor = hexToRgb(hexColor);
66
- * console.log(rgbColor) // 'rgb(255, 255, 255)'
67
- * ```
68
- *
69
- * @returns Color in RGB format string
70
- */
71
- hexToRgb = (hexColor: string): string => {
72
- const hex = hexColor.replace(/^#/, '')
73
- const bigint = parseInt(hex, 16)
74
- const red = (bigint >> 16) & 255
75
- const green = (bigint >> 8) & 255
76
- const blue = bigint & 255
77
- return `rgb(${red}, ${green}, ${blue})`
78
- }
79
-
80
- /**
81
- * @remarks
82
- * Checks whether a given HEX color is light or dark
83
- * @param hexColor - Color in HEX format
84
- * @example
85
- * ```ts
86
- * const hexColor = '#ffffff';
87
- * const isLight = isLightColor(hexColor);
88
- * console.log(isLight); // true or false
89
- * ```
90
- *
91
- * @returns True if the color is light, false if it is dark
92
- */
93
- isLightColor = (color: string): boolean => {
94
- // Remove spaces
95
- const trimmedColor = color.replace(/\s/g, '')
96
-
97
- // Check if the color is in RGBA format
98
- const rgbaMatch = trimmedColor.match(
99
- /^rgba\((\d+),(\d+),(\d+),(\d+(\.\d+)?)\)$/
100
- )
101
-
102
- if (rgbaMatch) {
103
- // Extract RGB components from RGBA
104
- const r = parseInt(rgbaMatch[1], 10)
105
- const g = parseInt(rgbaMatch[2], 10)
106
- const b = parseInt(rgbaMatch[3], 10)
107
-
108
- // Calculate the relative luminance without considering alpha
109
- const luminance = 0.299 * r + 0.587 * g + 0.114 * b
110
-
111
- // You can adjust the threshold to your preference
112
- const threshold = 128
113
-
114
- // Determine whether the color is light or dark
115
- return luminance > threshold
116
- }
117
-
118
- // Check if the color is in RGB format
119
- const rgbMatch = trimmedColor.match(/^rgb\((\d+),(\d+),(\d+)\)$/)
120
-
121
- if (rgbMatch) {
122
- // Extract RGB components from RGB
123
- const r = parseInt(rgbMatch[1], 10)
124
- const g = parseInt(rgbMatch[2], 10)
125
- const b = parseInt(rgbMatch[3], 10)
126
-
127
- // Calculate the relative luminance of the color
128
- // This formula gives more weight to green as the human eye is more sensitive to it
129
- const luminance = 0.299 * r + 0.587 * g + 0.114 * b
130
-
131
- // You can adjust the threshold to your preference
132
- const threshold = 128
133
-
134
- // Determine whether the color is light or dark
135
- return luminance > threshold
136
- }
137
-
138
- // Remove the '#' character if present
139
- const hexColor = trimmedColor.replace('#', '')
140
-
141
- // Check if the hex color has a valid format
142
- if (!/^[0-9A-Fa-f]{6}$/.test(hexColor)) {
143
- throw new Error(
144
- `Invalid color format '${color}'. Please provide a valid HEX, RGB, or RGBA color.`
145
- )
146
- }
147
-
148
- // Convert the hex color to RGB components
149
- const r = parseInt(hexColor.slice(0, 2), 16)
150
- const g = parseInt(hexColor.slice(2, 4), 16)
151
- const b = parseInt(hexColor.slice(4, 6), 16)
152
-
153
- // Calculate the relative luminance of the color
154
- // This formula gives more weight to green as the human eye is more sensitive to it
155
- const luminance = 0.299 * r + 0.587 * g + 0.114 * b
156
-
157
- // You can adjust the threshold to your preference
158
- const threshold = 128
159
-
160
- // Determine whether the color is light or dark
161
- return luminance > threshold
162
- }
163
- }
164
-
165
- const colorful = new Colorful()
166
-
167
- export default colorful
package/src/lib/cypto.ts DELETED
@@ -1,296 +0,0 @@
1
- /*
2
- Author : Omar Sawwas https://github.com/omarsawwas
3
- Date : 2023-06-17
4
- Description : RSA algorithm
5
- Version : 1.0
6
- */
7
- class Crypto {
8
- // Default variables
9
- private _primeNumberP = 17
10
- private _primeNumberQ = 19
11
- private _phiN = (this._primeNumberP - 1) * (this._primeNumberQ - 1)
12
- private _moduloDenominator = this._primeNumberP * this._primeNumberQ
13
-
14
- /**
15
- *
16
- * @param number - The number to check if prime
17
- * @returns The boolean answer of prime checking operation
18
- * @example
19
- * ```typescript
20
- * this._isPrime(5)
21
- * ```
22
- * */
23
-
24
- private _isPrime = (number: number): boolean => {
25
- if (number <= 1) {
26
- return false
27
- }
28
-
29
- if (number <= 3) {
30
- return true
31
- }
32
-
33
- if (number % 2 === 0 || number % 3 === 0) {
34
- return false
35
- }
36
-
37
- for (let i = 5; i * i <= number; i += 6) {
38
- if (number % i === 0 || number % (i + 2) === 0) {
39
- return false
40
- }
41
- }
42
-
43
- return true
44
- }
45
-
46
- /**
47
- * Set the P prime number for the RSA algorithm
48
- * @param primeP - The prime number P of type string
49
- * @returns void
50
- * @example
51
- * ```typescript
52
- * crypto.setPrimeP(17)
53
- * ```
54
- * */
55
-
56
- public setPrimeP(primeP: number) {
57
- if (isNaN(primeP) || primeP === 0) {
58
- throw new Error('Provide a valid number')
59
- }
60
-
61
- if (!this._isPrime(primeP)) {
62
- throw new Error('The number is not prime')
63
- }
64
- this._primeNumberP = primeP
65
- }
66
-
67
- /**
68
- * Set the Q prime number for the RSA algorithm
69
- * @param primeQ - The prime number Q of type string
70
- * @returns void
71
- * @example
72
- * ```typescript
73
- * crypto.setPrimeQ(19)
74
- * ```
75
- * */
76
-
77
- public setPrimeQ(primeQ: number) {
78
- if (isNaN(primeQ) || primeQ === 0) {
79
- throw new Error('Provide a valid number')
80
- }
81
- if (!this._isPrime(primeQ)) {
82
- throw new Error('The number is not prime')
83
- }
84
- this._primeNumberQ = primeQ
85
- }
86
-
87
- /**
88
- *
89
- * @param base - The base number of type number
90
- * @param exponent - The exponent number of type number
91
- * @returns The modular exponentiation of the base and the exponent
92
- * @example
93
- * ```typescript
94
- * this.modularExponentiation(2, 3)
95
- * ```
96
- * */
97
-
98
- private _modularExponentiation = (base: number, exponent: number) => {
99
- if (isNaN(base) || isNaN(exponent)) {
100
- throw new Error('Provide a valid number')
101
- }
102
- if (this._moduloDenominator === 1) {
103
- return 0
104
- }
105
- let result = 1
106
- base = base % this._moduloDenominator
107
- while (exponent > 0) {
108
- if (exponent % 2 === 1) {
109
- result = (result * base) % this._moduloDenominator
110
- }
111
- exponent = Math.floor(exponent / 2)
112
- base = (base * base) % this._moduloDenominator
113
- }
114
- return result
115
- }
116
-
117
- /**
118
- *
119
- * @param temporaryVarriableOne - The number one
120
- * @param temporaryVarriableTwo- The number two (phi n)
121
- * @returns The boolean answer of relatively prime checking operation with phi n
122
- * @example
123
- * ```typescript
124
- * this._areRelativelyPrime(5,9)
125
- * ```
126
- * */
127
-
128
- private _areRelativelyPrime = (
129
- temporaryVarriableOne: number,
130
- temporaryVarriableTwo: number
131
- ) => {
132
- while (temporaryVarriableTwo !== 0) {
133
- const temp = temporaryVarriableTwo
134
- temporaryVarriableTwo = temporaryVarriableOne % temporaryVarriableTwo
135
- temporaryVarriableOne = temp
136
- }
137
- return temporaryVarriableOne === 1
138
- }
139
-
140
- /**
141
- *
142
- * @returns The public and private key number which is a random number generated to match conditions set by RSA algorithm
143
- * @example
144
- * ```typescript
145
- * crypto.generateKeys()
146
- * ```
147
- * */
148
-
149
- public generateKeys = (): Record<'publicKey' | 'privateKey', number> => {
150
- const temporaryArray: number[] = []
151
- for (let i = 2; i < this._phiN; i++) {
152
- if (this._areRelativelyPrime(i, this._phiN)) {
153
- temporaryArray.push(i)
154
- }
155
- }
156
- if (temporaryArray.length === 0)
157
- throw new Error('No public key options found!')
158
- const randomIndex = Math.floor(Math.random() * temporaryArray.length)
159
- // return temporaryArray[randomIndex]
160
- const publicKey = temporaryArray[randomIndex]
161
- const privateKey = this._generatePrivateKey(publicKey)
162
- if (privateKey === publicKey) {
163
- return this.generateKeys()
164
- }
165
- return {
166
- privateKey: publicKey,
167
- publicKey: privateKey,
168
- }
169
- }
170
-
171
- /**
172
- *
173
- * @param publicKey - The public key number
174
- * @returns The private key number which is the modulus inverse with repect to modulo denominator
175
- * @example
176
- * ```typescript
177
- * crypto.generatePrivateKey(5)
178
- * ```
179
- * */
180
-
181
- private _generatePrivateKey = (publicKey: number): number => {
182
- if (publicKey <= 1 || isNaN(publicKey)) {
183
- throw new Error('Public key should be a number greater than 1')
184
- }
185
-
186
- if (!this._areRelativelyPrime(publicKey, this._phiN)) {
187
- throw new Error(
188
- 'Public key should be relatively prime with respect to phi N'
189
- )
190
- }
191
-
192
- let t1 = 0
193
- let t2 = 1
194
- let r1 = this._phiN
195
- let r2 = publicKey
196
-
197
- while (r2 !== 0) {
198
- const quotient = Math.floor(r1 / r2)
199
- const temp1 = t1
200
- const temp2 = r1
201
-
202
- t1 = t2
203
- r1 = r2
204
-
205
- t2 = temp1 - quotient * t2
206
- r2 = temp2 - quotient * r2
207
- }
208
-
209
- if (r1 > 1) {
210
- throw new Error('The number does not have a modular inverse.')
211
- }
212
-
213
- if (t1 < 0) {
214
- t1 += this._phiN
215
- }
216
-
217
- return t1
218
- }
219
-
220
- /**
221
- * Encrypt the message using the public key
222
- * @param message - The message to encrypt of type string|number|Record
223
- * @param publicKey - The public key to encrypt the message with of type number
224
- * @returns The encrypted message of type number[]
225
- * @example
226
- * ```typescript
227
- * crypto.encrypt('Hello World', 7)
228
- * ```
229
- * */
230
-
231
- public encrypt = (
232
- message: string | number | Record<any, any>,
233
- publicKey: number
234
- ): number[] => {
235
- if (isNaN(publicKey)) {
236
- throw new Error('Public key should be a number')
237
- }
238
- if (publicKey <= 0) {
239
- throw new Error(
240
- 'Private key should be a positive number different than 0'
241
- )
242
- }
243
- if (typeof message === 'number') {
244
- message = message.toString()
245
- }
246
- if (typeof message === 'object') {
247
- message = JSON.stringify(message)
248
- }
249
- const encryptedMessage: number[] = []
250
- for (let i = 0; i < message.length; i++) {
251
- const a = message.charCodeAt(i)
252
- encryptedMessage.push(this._modularExponentiation(a, publicKey))
253
- }
254
- return encryptedMessage
255
- }
256
-
257
- /**
258
- * Decrypt the message using the private key
259
- * @param encryptedMessage - The encryptedMessage to decrypt of type number[]
260
- * @param privateKey - The private key to decrypt the message with of type number
261
- * @returns The decrypted message of type string
262
- * @example
263
- * ```typescript
264
- * crypto.decrypt([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100], 23)
265
- * ```
266
- * */
267
- public decrypt = (encryptedMessage: number[], privateKey: number): string => {
268
- if (isNaN(privateKey)) {
269
- throw new Error('Public key should be a number')
270
- }
271
- if (privateKey <= 0) {
272
- throw new Error(
273
- 'Private key should be a positive number different than 0'
274
- )
275
- }
276
- if (!Array.isArray(encryptedMessage)) {
277
- throw new Error('Encrypted message should be an array')
278
- }
279
- const allValidNumbers = encryptedMessage.every((number) => !isNaN(number))
280
- if (!allValidNumbers) {
281
- throw new Error('Encrypted message should be an array of numbers')
282
- }
283
- const decryptedAsciiMessage: number[] = []
284
- for (let i = 0; i < encryptedMessage.length; i++) {
285
- const a = encryptedMessage[i]
286
- decryptedAsciiMessage.push(this._modularExponentiation(a, privateKey))
287
- }
288
- let finalString = ''
289
- for (let i = 0; i < decryptedAsciiMessage.length; i++) {
290
- finalString += String.fromCharCode(decryptedAsciiMessage[i])
291
- }
292
- return finalString
293
- }
294
- }
295
- const crypto = new Crypto()
296
- export default crypto