@beecode/msh-util 1.2.0 → 1.2.1
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/lib/array-util.d.ts +2 -2
- package/lib/array-util.js +2 -2
- package/package.json +134 -127
- package/src/array-util.test.ts +50 -0
- package/src/array-util.ts +26 -0
- package/src/class-factory-pattern.test.ts +39 -0
- package/src/class-factory-pattern.ts +29 -0
- package/src/express/error-handler.test.ts +44 -0
- package/src/express/error-handler.ts +25 -0
- package/src/index.ts +25 -0
- package/src/joi-util.test.ts +192 -0
- package/src/joi-util.ts +65 -0
- package/src/memoize-factory.test.ts +40 -0
- package/src/memoize-factory.ts +27 -0
- package/src/object-util.test.ts +360 -0
- package/src/object-util.ts +127 -0
- package/src/regex-util.test.ts +25 -0
- package/src/regex-util.ts +11 -0
- package/src/single-threshold-promise.test.ts +91 -0
- package/src/single-threshold-promise.ts +56 -0
- package/src/singleton/async.test.ts +122 -0
- package/src/singleton/async.ts +90 -0
- package/src/singleton/pattern.test.ts +16 -0
- package/src/singleton/pattern.ts +44 -0
- package/src/string-util.test.ts +18 -0
- package/src/string-util.ts +18 -0
- package/src/time-util.test.ts +89 -0
- package/src/time-util.ts +98 -0
- package/src/timeout.test.ts +65 -0
- package/src/timeout.ts +16 -0
- package/src/type-util.test.ts +20 -0
- package/src/type-util.ts +54 -0
- package/src/types/any-function/index.ts +1 -0
- package/src/types/any-function/no-params.ts +1 -0
- package/src/types/any-function/promise-no-params.ts +1 -0
- package/src/types/any-function/promise.ts +1 -0
- package/src/types/types.d.ts +2 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { DurationUnit, DurationUnitType, TimeUtil } from 'src/time-util'
|
|
2
|
+
|
|
3
|
+
describe('TimeUtil', () => {
|
|
4
|
+
const constantNowDate = new Date('2022-01-01T00:00:00.000Z')
|
|
5
|
+
const constantNowUnix = 1640995200000 // 2022-01-01T00:00:00 UTC
|
|
6
|
+
const constantNowUnixSec = 1640995200 // 2022-01-01T00:00:00 UTC
|
|
7
|
+
const timeUtil = new TimeUtil()
|
|
8
|
+
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
jest.useFakeTimers('modern' as any)
|
|
11
|
+
jest.setSystemTime(constantNowDate.getTime())
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
afterAll(() => {
|
|
15
|
+
jest.useRealTimers()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('now', () => {
|
|
19
|
+
it('should return mocked date', () => {
|
|
20
|
+
expect(timeUtil.now().toISOString()).toEqual(constantNowDate.toISOString())
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('dateToUnix', () => {
|
|
25
|
+
it('should convert constant date to constant unix', () => {
|
|
26
|
+
expect(timeUtil.dateToUnix(timeUtil.now())).toEqual(constantNowUnix)
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('dateToUnixSec', () => {
|
|
31
|
+
it('should convert constant date to constant unixSec', () => {
|
|
32
|
+
expect(timeUtil.dateToUnixSec(timeUtil.now())).toEqual(constantNowUnixSec)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
describe('unixToDate', () => {
|
|
37
|
+
it('should convert constant date to constant unix', () => {
|
|
38
|
+
expect(timeUtil.unixToDate(constantNowUnix)).toEqual(timeUtil.now())
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('unixSecToDate', () => {
|
|
43
|
+
it('should convert constant date to constant unixSec', () => {
|
|
44
|
+
expect(timeUtil.unixSecToDate(constantNowUnixSec)).toEqual(timeUtil.now())
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('addToDate', () => {
|
|
49
|
+
it.each([
|
|
50
|
+
[DurationUnit.MILLISECOND, 1, new Date('2022-01-01T00:00:00.001Z')],
|
|
51
|
+
[DurationUnit.SECOND, 1, new Date('2022-01-01T00:00:01.000Z')],
|
|
52
|
+
[DurationUnit.MINUTE, 1, new Date('2022-01-01T00:01:00.000Z')],
|
|
53
|
+
[DurationUnit.HOUR, 1, new Date('2022-01-01T01:00:00.000Z')],
|
|
54
|
+
[DurationUnit.DAY, 1, new Date('2022-01-02T00:00:00.000Z')],
|
|
55
|
+
[DurationUnit.WEEK, 1, new Date('2022-01-08T00:00:00.000Z')],
|
|
56
|
+
[DurationUnit.MONTH, 1, new Date('2022-02-01T00:00:00.000Z')],
|
|
57
|
+
[DurationUnit.YEAR, 1, new Date('2023-01-01T00:00:00.000Z')],
|
|
58
|
+
[DurationUnit.MILLISECOND, -1, new Date('2021-12-31T23:59:59.999Z')],
|
|
59
|
+
[DurationUnit.SECOND, -1, new Date('2021-12-31T23:59:59.000Z')],
|
|
60
|
+
[DurationUnit.MINUTE, -1, new Date('2021-12-31T23:59:00.000Z')],
|
|
61
|
+
[DurationUnit.HOUR, -1, new Date('2021-12-31T23:00:00.000Z')],
|
|
62
|
+
[DurationUnit.DAY, -1, new Date('2021-12-31T00:00:00.000Z')],
|
|
63
|
+
[DurationUnit.WEEK, -1, new Date('2021-12-25T00:00:00.000Z')],
|
|
64
|
+
[DurationUnit.MONTH, -1, new Date('2021-12-01T00:00:00.000Z')],
|
|
65
|
+
[DurationUnit.YEAR, -1, new Date('2021-01-01T00:00:00.000Z')],
|
|
66
|
+
['MILLISECOND', 1, new Date('2022-01-01T00:00:00.001Z')],
|
|
67
|
+
['SECOND', 1, new Date('2022-01-01T00:00:01.000Z')],
|
|
68
|
+
['MINUTE', 1, new Date('2022-01-01T00:01:00.000Z')],
|
|
69
|
+
['HOUR', 1, new Date('2022-01-01T01:00:00.000Z')],
|
|
70
|
+
['DAY', 1, new Date('2022-01-02T00:00:00.000Z')],
|
|
71
|
+
['WEEK', 1, new Date('2022-01-08T00:00:00.000Z')],
|
|
72
|
+
['MONTH', 1, new Date('2022-02-01T00:00:00.000Z')],
|
|
73
|
+
['YEAR', 1, new Date('2023-01-01T00:00:00.000Z')],
|
|
74
|
+
['MILLISECOND', -1, new Date('2021-12-31T23:59:59.999Z')],
|
|
75
|
+
['SECOND', -1, new Date('2021-12-31T23:59:59.000Z')],
|
|
76
|
+
['MINUTE', -1, new Date('2021-12-31T23:59:00.000Z')],
|
|
77
|
+
['HOUR', -1, new Date('2021-12-31T23:00:00.000Z')],
|
|
78
|
+
['DAY', -1, new Date('2021-12-31T00:00:00.000Z')],
|
|
79
|
+
['WEEK', -1, new Date('2021-12-25T00:00:00.000Z')],
|
|
80
|
+
['MONTH', -1, new Date('2021-12-01T00:00:00.000Z')],
|
|
81
|
+
['YEAR', -1, new Date('2021-01-01T00:00:00.000Z')],
|
|
82
|
+
] as [DurationUnitType | DurationUnit, number, Date][])(
|
|
83
|
+
'%#. should increase date by unit %s and value %s and get date $s',
|
|
84
|
+
(unit, value, expected) => {
|
|
85
|
+
expect(timeUtil.addToDate({ date: timeUtil.now(), unit, value })).toEqual(expected)
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
})
|
|
89
|
+
})
|
package/src/time-util.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import add from 'date-fns/add'
|
|
2
|
+
import addMilliseconds from 'date-fns/addMilliseconds'
|
|
3
|
+
import format from 'date-fns/format'
|
|
4
|
+
import parse from 'date-fns/parse'
|
|
5
|
+
|
|
6
|
+
export enum DurationUnit {
|
|
7
|
+
MILLISECOND = 'MILLISECOND',
|
|
8
|
+
SECOND = 'SECOND',
|
|
9
|
+
MINUTE = 'MINUTE',
|
|
10
|
+
HOUR = 'HOUR',
|
|
11
|
+
DAY = 'DAY',
|
|
12
|
+
WEEK = 'WEEK',
|
|
13
|
+
MONTH = 'MONTH',
|
|
14
|
+
YEAR = 'YEAR',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type DurationUnitType = `${DurationUnit}`
|
|
18
|
+
|
|
19
|
+
export class TimeUtil {
|
|
20
|
+
/**
|
|
21
|
+
* return date object with the current time
|
|
22
|
+
* @return {Date}
|
|
23
|
+
* @example
|
|
24
|
+
* console.log(new TimeUtil().now().toISOString()) // 2023-03-08T19:45:01.991Z
|
|
25
|
+
*/
|
|
26
|
+
now(): Date {
|
|
27
|
+
return new Date()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Convert date object to unix timestamp (milliseconds)
|
|
32
|
+
* @param {Date} date
|
|
33
|
+
* @return {number}
|
|
34
|
+
* @example
|
|
35
|
+
* // timeUtil.now().toISOString() === 2023-03-08T19:45:01.991Z
|
|
36
|
+
* const timeUtil = new TimeUtil()
|
|
37
|
+
* console.log(timeUtil.dateToUnix(timeUtil.now())) // 1678304701991
|
|
38
|
+
*/
|
|
39
|
+
dateToUnix(date: Date): number {
|
|
40
|
+
return +format(date, 'T')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert date object to unix timestamp (seconds)
|
|
45
|
+
* @param {Date} date
|
|
46
|
+
* @return {number}
|
|
47
|
+
* @example
|
|
48
|
+
* // timeUtil.now().toISOString() === 2023-03-08T19:45:01.991Z
|
|
49
|
+
* const timeUtil = new TimeUtil()
|
|
50
|
+
* console.log(timeUtil.dateToUnix(timeUtil.now())) // 1678304701
|
|
51
|
+
*/
|
|
52
|
+
dateToUnixSec(date: Date): number {
|
|
53
|
+
return +format(date, 't')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Convert unix timestamp (milliseconds) to date object
|
|
58
|
+
* @param {number} unix
|
|
59
|
+
* @return {Date}
|
|
60
|
+
* @example
|
|
61
|
+
* const timeUtil = new TimeUtil()
|
|
62
|
+
* console.log(timeUtil.unixToDate(1678304701991).toISOString()) // 2023-03-08T19:45:01.991Z
|
|
63
|
+
*/
|
|
64
|
+
unixToDate(unix: number): Date {
|
|
65
|
+
return parse(unix.toString(), 'T', this.now())
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Convert unix timestamp (seconds) to date object
|
|
70
|
+
* @param {number} unix
|
|
71
|
+
* @return {Date}
|
|
72
|
+
* @example
|
|
73
|
+
* const timeUtil = new TimeUtil()
|
|
74
|
+
* console.log(timeUtil.unixToDate(1678304701).toISOString()) // 2023-03-08T19:45:01.000Z
|
|
75
|
+
*/
|
|
76
|
+
unixSecToDate(unix: number): Date {
|
|
77
|
+
return parse(unix.toString(), 't', this.now())
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Change the value of date by unit/value pare.
|
|
82
|
+
* @param {{units: DurationUnitType, value: number, date: Date}} params
|
|
83
|
+
* @return {Date}
|
|
84
|
+
* @example
|
|
85
|
+
* // timeUtil.now().toISOString() === 2023-03-08T19:45:01.991Z
|
|
86
|
+
* const timeUtil = new TimeUtil()
|
|
87
|
+
* console.log(timeUtil.addToDate({date: timeUtil.now(), unit: 'DAY', value: 1 }).toISOString()) // 2023-03-09T19:45:01.991Z
|
|
88
|
+
* console.log(timeUtil.addToDate({date: timeUtil.now(), unit: DurationUnit.MONTH, value: -1 }).toISOString()) //2023-02-08T19:45:01.991Z
|
|
89
|
+
*/
|
|
90
|
+
addToDate(params: { unit: DurationUnitType | DurationUnit; value: number; date: Date }): Date {
|
|
91
|
+
const { date, unit, value } = params
|
|
92
|
+
if (`${unit}` === 'MILLISECOND') {
|
|
93
|
+
return addMilliseconds(date, value)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return add(date, { [`${unit.toLowerCase()}s`]: value })
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { timeout } from 'src/timeout'
|
|
2
|
+
|
|
3
|
+
describe('timeout', () => {
|
|
4
|
+
const fn_one = jest.fn()
|
|
5
|
+
const fn_two = jest.fn()
|
|
6
|
+
const fn_three = jest.fn()
|
|
7
|
+
const fn_onResolve = jest.fn()
|
|
8
|
+
|
|
9
|
+
const timeoutImplementation = async (): Promise<void> => {
|
|
10
|
+
fn_one()
|
|
11
|
+
await timeout(1000)
|
|
12
|
+
fn_two()
|
|
13
|
+
await timeout(1000)
|
|
14
|
+
fn_three()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
jest.useFakeTimers()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
jest.clearAllTimers()
|
|
23
|
+
jest.useRealTimers()
|
|
24
|
+
jest.resetAllMocks()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('should function one by one with pause of 1000ms', async () => {
|
|
28
|
+
expect(fn_one).toHaveBeenCalledTimes(0)
|
|
29
|
+
expect(fn_two).toHaveBeenCalledTimes(0)
|
|
30
|
+
expect(fn_three).toHaveBeenCalledTimes(0)
|
|
31
|
+
expect(fn_onResolve).toHaveBeenCalledTimes(0)
|
|
32
|
+
|
|
33
|
+
const promise1 = timeoutImplementation().then(() => {
|
|
34
|
+
return fn_onResolve()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
expect(fn_one).toHaveBeenCalledTimes(1)
|
|
38
|
+
expect(fn_two).toHaveBeenCalledTimes(0)
|
|
39
|
+
expect(fn_three).toHaveBeenCalledTimes(0)
|
|
40
|
+
expect(fn_onResolve).toHaveBeenCalledTimes(0)
|
|
41
|
+
|
|
42
|
+
jest.advanceTimersByTime(1000)
|
|
43
|
+
await Promise.resolve()
|
|
44
|
+
|
|
45
|
+
expect(fn_one).toHaveBeenCalledTimes(1)
|
|
46
|
+
expect(fn_two).toHaveBeenCalledTimes(1)
|
|
47
|
+
expect(fn_three).toHaveBeenCalledTimes(0)
|
|
48
|
+
expect(fn_onResolve).toHaveBeenCalledTimes(0)
|
|
49
|
+
|
|
50
|
+
jest.advanceTimersByTime(1000)
|
|
51
|
+
await Promise.resolve()
|
|
52
|
+
|
|
53
|
+
expect(fn_one).toHaveBeenCalledTimes(1)
|
|
54
|
+
expect(fn_two).toHaveBeenCalledTimes(1)
|
|
55
|
+
expect(fn_three).toHaveBeenCalledTimes(1)
|
|
56
|
+
expect(fn_onResolve).toHaveBeenCalledTimes(0)
|
|
57
|
+
|
|
58
|
+
await promise1
|
|
59
|
+
|
|
60
|
+
expect(fn_one).toHaveBeenCalledTimes(1)
|
|
61
|
+
expect(fn_two).toHaveBeenCalledTimes(1)
|
|
62
|
+
expect(fn_three).toHaveBeenCalledTimes(1)
|
|
63
|
+
expect(fn_onResolve).toHaveBeenCalledTimes(1)
|
|
64
|
+
})
|
|
65
|
+
})
|
package/src/timeout.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {number} ms The time, in milliseconds that the timer should wait before the specified function or code is executed. If this parameter is omitted, a value of 0 is used, meaning execute "immediately", or more accurately, the next event cycle.
|
|
4
|
+
* @return {Promise<void>}
|
|
5
|
+
* @example
|
|
6
|
+
* const lightBlink = (): void => {
|
|
7
|
+
* turnLightOn()
|
|
8
|
+
* timeout(3000) // wait for 3 seconds
|
|
9
|
+
* turnLightOff()
|
|
10
|
+
* timeout(3000) // wait for 3 seconds
|
|
11
|
+
* turnLightOn()
|
|
12
|
+
* }
|
|
13
|
+
*/
|
|
14
|
+
export const timeout = (ms: number): Promise<void> => {
|
|
15
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { typeUtil } from 'src/type-util'
|
|
2
|
+
|
|
3
|
+
describe('typeUtil', () => {
|
|
4
|
+
describe('exhaustiveMessage', () => {
|
|
5
|
+
it('should return string', () => {
|
|
6
|
+
const testMessage = 'testMessage'
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
expect(typeUtil.exhaustiveMessage(testMessage, '')).toEqual(testMessage)
|
|
9
|
+
})
|
|
10
|
+
})
|
|
11
|
+
describe('exhaustiveError', () => {
|
|
12
|
+
it('should return string', () => {
|
|
13
|
+
const testMessage = 'testMessage'
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
const error = typeUtil.exhaustiveError(testMessage, '')
|
|
16
|
+
expect(error).toBeInstanceOf(Error)
|
|
17
|
+
expect(error.message).toEqual(testMessage)
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
})
|
package/src/type-util.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const typeUtil = {
|
|
2
|
+
/**
|
|
3
|
+
* This is the similar to exhaustiveMessage, but instead of message we are returning error so we can throw it
|
|
4
|
+
* @param {string} message
|
|
5
|
+
* @param {never} _
|
|
6
|
+
* @return {Error}
|
|
7
|
+
* @example
|
|
8
|
+
* export type Animal = 'cat' | 'dog' | 'bird';
|
|
9
|
+
*
|
|
10
|
+
* export const makeSound = (animal: Animal): string => {
|
|
11
|
+
* switch (animal) {
|
|
12
|
+
* case 'cat':
|
|
13
|
+
* return 'Meow'
|
|
14
|
+
* case 'dog':
|
|
15
|
+
* return 'Woof'
|
|
16
|
+
* case 'bird':
|
|
17
|
+
* return 'Tweet'
|
|
18
|
+
* default:
|
|
19
|
+
* throw typeUtil.exhaustiveError('Unknown animal [animal]', animal)
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
exhaustiveError: (message: string, _: never): Error => {
|
|
24
|
+
return new Error(message)
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* In TypeScript, exhaustiveMessage is a technique that can be used with switch statements to ensure that all possible cases are handled.
|
|
29
|
+
*
|
|
30
|
+
* When using switch statements, it is common to have a default case that handles any unanticipated cases. However, sometimes it is important to ensure that all cases are explicitly handled to avoid potential errors or bugs in the code.
|
|
31
|
+
* @param {string} message
|
|
32
|
+
* @param {never} _
|
|
33
|
+
* @return {string}
|
|
34
|
+
* @example
|
|
35
|
+
* export type Animal = 'cat' | 'dog' | 'bird';
|
|
36
|
+
*
|
|
37
|
+
* export const makeSound = (animal: Animal): string => {
|
|
38
|
+
* switch (animal) {
|
|
39
|
+
* case 'cat':
|
|
40
|
+
* return 'Meow'
|
|
41
|
+
* case 'dog':
|
|
42
|
+
* return 'Woof'
|
|
43
|
+
* case 'bird':
|
|
44
|
+
* return 'Tweet'
|
|
45
|
+
* default:
|
|
46
|
+
* console.error(new TypeUtil().exhaustiveMessage('Unknown animal [animal]', animal))
|
|
47
|
+
* return 'unknown sound'
|
|
48
|
+
* }
|
|
49
|
+
* }
|
|
50
|
+
*/
|
|
51
|
+
exhaustiveMessage: (message: string, _: never): string => {
|
|
52
|
+
return message
|
|
53
|
+
},
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type AnyFunction<T> = (...args: any[]) => T
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type AnyFunctionNoParams<T> = () => T
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type AnyFunctionPromiseNoParams<T> = () => Promise<T>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type AnyFunctionPromise<T> = (...args: any[]) => Promise<T>
|