@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.
- package/ReleaseNotes.md +17 -1
- package/dist/__tests__/elastic-transport.test.js +13 -0
- package/dist/__tests__/logger.test.js +21 -0
- package/dist/lib/elastic-transport.js +3 -1
- package/dist/lib/logger.js +4 -3
- package/dist/types/logger.d.ts +2 -1
- package/package.json +9 -3
- package/.github/workflows/npm-publish.yml +0 -84
- package/.github/workflows/push.yml +0 -83
- package/.husky/pre-commit +0 -62
- package/.prettierrc +0 -8
- package/.prettierrc.json +0 -8
- package/ISSUES.md +0 -0
- package/eslint.config.mjs +0 -64
- package/example.env +0 -6
- package/jest.config.js +0 -13
- package/src/__tests__/colorful.test.ts +0 -77
- package/src/__tests__/elastic-transport.test.ts +0 -94
- package/src/__tests__/formatter.test.ts +0 -96
- package/src/__tests__/logger.test.ts +0 -122
- package/src/env.d.ts +0 -9
- package/src/index.ts +0 -1
- package/src/lib/colorful.ts +0 -167
- package/src/lib/cypto.ts +0 -296
- package/src/lib/elastic-transport.ts +0 -356
- package/src/lib/formatter.ts +0 -125
- package/src/lib/imagefull.ts +0 -41
- package/src/lib/index.ts +0 -12
- package/src/lib/logger.ts +0 -600
- package/src/lib/pdf.ts +0 -25
- package/src/lib/trace-store.ts +0 -46
- package/src/types/index.ts +0 -1
- package/src/types/logger.ts +0 -66
- package/src/utilities/axios.ts +0 -4
- package/src/utilities/index.ts +0 -1
- package/tsconfig.json +0 -16
|
@@ -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
package/src/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './lib'
|
package/src/lib/colorful.ts
DELETED
|
@@ -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
|