@zairakai/js-utils 1.0.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/LICENSE +21 -0
- package/README.md +270 -0
- package/dist/arrays.cjs +210 -0
- package/dist/arrays.d.cts +119 -0
- package/dist/arrays.d.ts +119 -0
- package/dist/arrays.js +32 -0
- package/dist/chunk-27YHP2CK.js +407 -0
- package/dist/chunk-3WNRYKPG.js +37 -0
- package/dist/chunk-42CHLXT7.js +214 -0
- package/dist/chunk-6F4PWJZI.js +0 -0
- package/dist/chunk-7SXRFZBB.js +173 -0
- package/dist/chunk-F6RSTW65.js +156 -0
- package/dist/chunk-G7ZJ23DW.js +253 -0
- package/dist/chunk-IPP7PA6H.js +136 -0
- package/dist/chunk-LDSWHSRX.js +96 -0
- package/dist/chunk-TY75OOIQ.js +700 -0
- package/dist/chunk-W6JEMFAF.js +54 -0
- package/dist/chunk-XEJLBAXE.js +164 -0
- package/dist/chunk-Z7G3SIQH.js +270 -0
- package/dist/chunk-ZJPKS2MQ.js +101 -0
- package/dist/collections.cjs +797 -0
- package/dist/collections.d.cts +353 -0
- package/dist/collections.d.ts +353 -0
- package/dist/collections.js +17 -0
- package/dist/datetime.cjs +80 -0
- package/dist/datetime.d.cts +75 -0
- package/dist/datetime.d.ts +75 -0
- package/dist/datetime.js +24 -0
- package/dist/equals.cjs +121 -0
- package/dist/equals.d.cts +24 -0
- package/dist/equals.d.ts +24 -0
- package/dist/equals.js +8 -0
- package/dist/formatters.cjs +201 -0
- package/dist/formatters.d.cts +180 -0
- package/dist/formatters.d.ts +180 -0
- package/dist/formatters.js +48 -0
- package/dist/index.cjs +2906 -0
- package/dist/index.d.cts +120 -0
- package/dist/index.d.ts +120 -0
- package/dist/index.js +348 -0
- package/dist/number.cjs +279 -0
- package/dist/number.d.cts +177 -0
- package/dist/number.d.ts +177 -0
- package/dist/number.js +10 -0
- package/dist/obj.cjs +427 -0
- package/dist/obj.d.cts +177 -0
- package/dist/obj.d.ts +177 -0
- package/dist/obj.js +12 -0
- package/dist/php-arrays.cjs +954 -0
- package/dist/php-arrays.d.cts +256 -0
- package/dist/php-arrays.d.ts +256 -0
- package/dist/php-arrays.js +70 -0
- package/dist/runtime.cjs +134 -0
- package/dist/runtime.d.cts +90 -0
- package/dist/runtime.d.ts +90 -0
- package/dist/runtime.js +24 -0
- package/dist/schemas.cjs +86 -0
- package/dist/schemas.d.cts +108 -0
- package/dist/schemas.d.ts +108 -0
- package/dist/schemas.js +22 -0
- package/dist/str.cjs +499 -0
- package/dist/str.d.cts +282 -0
- package/dist/str.d.ts +282 -0
- package/dist/str.js +11 -0
- package/dist/types.cjs +18 -0
- package/dist/types.d.cts +13 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.js +1 -0
- package/dist/validator.cjs +251 -0
- package/dist/validator.d.cts +99 -0
- package/dist/validator.d.ts +99 -0
- package/dist/validator.js +11 -0
- package/dist/validators.cjs +217 -0
- package/dist/validators.d.cts +216 -0
- package/dist/validators.d.ts +216 -0
- package/dist/validators.js +64 -0
- package/package.json +180 -0
- package/src/arrays.ts +316 -0
- package/src/collections.ts +866 -0
- package/src/datetime.ts +103 -0
- package/src/equals.ts +134 -0
- package/src/formatters.ts +342 -0
- package/src/index.ts +36 -0
- package/src/number.ts +281 -0
- package/src/obj.ts +303 -0
- package/src/php-arrays.ts +445 -0
- package/src/pipe.ts +29 -0
- package/src/runtime.ts +194 -0
- package/src/schemas.ts +136 -0
- package/src/str.ts +438 -0
- package/src/types.ts +13 -0
- package/src/validator.ts +157 -0
- package/src/validators.ts +359 -0
package/src/datetime.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Date and time utilities using Day.js
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import dayjsModule, { ConfigType, Dayjs } from 'dayjs'
|
|
6
|
+
import isBetween from 'dayjs/plugin/isBetween'
|
|
7
|
+
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
|
|
8
|
+
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
|
|
9
|
+
import relativeTime from 'dayjs/plugin/relativeTime'
|
|
10
|
+
import utc from 'dayjs/plugin/utc'
|
|
11
|
+
|
|
12
|
+
// Configure dayjs with commonly used plugins
|
|
13
|
+
dayjsModule.extend(isBetween)
|
|
14
|
+
dayjsModule.extend(isSameOrAfter)
|
|
15
|
+
dayjsModule.extend(isSameOrBefore)
|
|
16
|
+
dayjsModule.extend(relativeTime)
|
|
17
|
+
dayjsModule.extend(utc)
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Configured Dayjs instance.
|
|
21
|
+
*/
|
|
22
|
+
export const dayjs = dayjsModule
|
|
23
|
+
|
|
24
|
+
// Utility functions
|
|
25
|
+
/**
|
|
26
|
+
* Get the current date and time as a Dayjs instance.
|
|
27
|
+
*
|
|
28
|
+
* @returns {Dayjs} Current date and time
|
|
29
|
+
*/
|
|
30
|
+
export const now = (): Dayjs => dayjs()
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get today's date at the start of the day.
|
|
34
|
+
*
|
|
35
|
+
* @returns {Dayjs} Today's date
|
|
36
|
+
*/
|
|
37
|
+
export const today = (): Dayjs => dayjs().startOf('day')
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get tomorrow's date at the start of the day.
|
|
41
|
+
*
|
|
42
|
+
* @returns {Dayjs} Tomorrow's date
|
|
43
|
+
*/
|
|
44
|
+
export const tomorrow = (): Dayjs => dayjs().add(1, 'day').startOf('day')
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get yesterday's date at the start of the day.
|
|
48
|
+
*
|
|
49
|
+
* @returns {Dayjs} Yesterday's date
|
|
50
|
+
*/
|
|
51
|
+
export const yesterday = (): Dayjs => dayjs().subtract(1, 'day').startOf('day')
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if a date is between two other dates.
|
|
55
|
+
*
|
|
56
|
+
* @param {ConfigType} date Date to check
|
|
57
|
+
* @param {ConfigType} start Start date
|
|
58
|
+
* @param {ConfigType} end End date
|
|
59
|
+
* @param {dayjsModule.OpUnitType} [unit='day'] Unit of comparison (day, month, year, etc.)
|
|
60
|
+
* @param {'()' | '[]' | '[)' | '(]'} [inclusivity='[]'] Inclusivity
|
|
61
|
+
* @returns {boolean} True if the date is between start and end
|
|
62
|
+
*/
|
|
63
|
+
export const isBetweenDates = (
|
|
64
|
+
date: ConfigType,
|
|
65
|
+
start: ConfigType,
|
|
66
|
+
end: ConfigType,
|
|
67
|
+
unit: dayjsModule.OpUnitType = 'day',
|
|
68
|
+
inclusivity: '()' | '[]' | '[)' | '(]' = '[]'
|
|
69
|
+
): boolean => {
|
|
70
|
+
return dayjs(date).isBetween(dayjs(start), dayjs(end), unit, inclusivity)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Format a date to a human-readable relative time.
|
|
75
|
+
*
|
|
76
|
+
* @param {ConfigType} date Date to format
|
|
77
|
+
* @returns {string} Relative time (e.g., "2 hours ago")
|
|
78
|
+
*/
|
|
79
|
+
export const fromNow = (date: ConfigType): string => dayjs(date).fromNow()
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check if a date is today.
|
|
83
|
+
*
|
|
84
|
+
* @param {ConfigType} date Date to check
|
|
85
|
+
* @returns {boolean} True if the date is today
|
|
86
|
+
*/
|
|
87
|
+
export const isToday = (date: ConfigType): boolean => dayjs(date).isSame(dayjs(), 'day')
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if a date is in the past.
|
|
91
|
+
*
|
|
92
|
+
* @param {ConfigType} date Date to check
|
|
93
|
+
* @returns {boolean} True if the date is in the past
|
|
94
|
+
*/
|
|
95
|
+
export const isPast = (date: ConfigType): boolean => dayjs(date).isBefore(dayjs())
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if a date is in the future.
|
|
99
|
+
*
|
|
100
|
+
* @param {ConfigType} date Date to check
|
|
101
|
+
* @returns {boolean} True if the date is in the future
|
|
102
|
+
*/
|
|
103
|
+
export const isFuture = (date: ConfigType): boolean => dayjs(date).isAfter(dayjs())
|
package/src/equals.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { GenericRecord } from './types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Deep equality and comparison utilities
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Perform a deep comparison between two values to determine if they are equivalent.
|
|
9
|
+
*
|
|
10
|
+
* @param {unknown} a The first value to compare
|
|
11
|
+
* @param {unknown} b The second value to compare
|
|
12
|
+
* @returns {boolean} True if the values are equivalent, false otherwise
|
|
13
|
+
*/
|
|
14
|
+
export const isEqual = (a: unknown, b: unknown): boolean => {
|
|
15
|
+
if (a === b) {
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (a && b && 'object' === typeof a && 'object' === typeof b) {
|
|
20
|
+
if (a.constructor !== b.constructor) {
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (Array.isArray(a)) {
|
|
25
|
+
if (a.length !== (b as unknown[]).length) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
for (let i = 0; i < a.length; i++) {
|
|
29
|
+
if (!isEqual(a[i], (b as unknown[])[i])) {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (a instanceof Map && b instanceof Map) {
|
|
37
|
+
if (a.size !== b.size) {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
for (const [key, val] of a) {
|
|
41
|
+
if (!b.has(key) || !isEqual(val, b.get(key))) {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (a instanceof Set && b instanceof Set) {
|
|
49
|
+
if (a.size !== b.size) {
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
for (const val of a) {
|
|
53
|
+
if (!b.has(val)) {
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (a.constructor === RegExp) {
|
|
61
|
+
return (a as RegExp).source === (b as RegExp).source && (a as RegExp).flags === (b as RegExp).flags
|
|
62
|
+
}
|
|
63
|
+
if (a.valueOf !== Object.prototype.valueOf) {
|
|
64
|
+
return a.valueOf() === b.valueOf()
|
|
65
|
+
}
|
|
66
|
+
if (a.toString !== Object.prototype.toString) {
|
|
67
|
+
return a.toString() === b.toString()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const keys = Object.keys(a)
|
|
71
|
+
if (keys.length !== Object.keys(b).length) {
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const bObj = b as GenericRecord
|
|
76
|
+
const aObj = a as GenericRecord
|
|
77
|
+
|
|
78
|
+
for (const key of keys) {
|
|
79
|
+
if (!Object.prototype.hasOwnProperty.call(bObj, key)) {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
if (!isEqual(aObj[key], bObj[key])) {
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return true
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return a !== a && b !== b // Handle NaN
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Find the differences between two objects.
|
|
95
|
+
* Returns an object containing only the keys that differ.
|
|
96
|
+
*
|
|
97
|
+
* @param {unknown} original The original object
|
|
98
|
+
* @param {unknown} current The current object
|
|
99
|
+
* @returns {GenericRecord} An object containing the differences
|
|
100
|
+
*/
|
|
101
|
+
export const diff = (original: unknown, current: unknown): GenericRecord => {
|
|
102
|
+
const result: GenericRecord = {}
|
|
103
|
+
|
|
104
|
+
if (isEqual(original, current)) {
|
|
105
|
+
return result
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!original || !current || 'object' !== typeof original || 'object' !== typeof current) {
|
|
109
|
+
return (current as GenericRecord) || {}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const originalObj = original as GenericRecord
|
|
113
|
+
const currentObj = current as GenericRecord
|
|
114
|
+
|
|
115
|
+
const keys = new Set([...Object.keys(originalObj), ...Object.keys(currentObj)])
|
|
116
|
+
|
|
117
|
+
for (const key of keys) {
|
|
118
|
+
if (!isEqual(originalObj[key], currentObj[key])) {
|
|
119
|
+
if (
|
|
120
|
+
originalObj[key] &&
|
|
121
|
+
currentObj[key] &&
|
|
122
|
+
'object' === typeof originalObj[key] &&
|
|
123
|
+
'object' === typeof currentObj[key] &&
|
|
124
|
+
!Array.isArray(originalObj[key])
|
|
125
|
+
) {
|
|
126
|
+
result[key] = diff(originalObj[key], currentObj[key])
|
|
127
|
+
} else {
|
|
128
|
+
result[key] = currentObj[key]
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return result
|
|
134
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String manipulation and formatting utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Capitalize the first letter of a string and lowercase the rest.
|
|
7
|
+
*
|
|
8
|
+
* @param {unknown} value The value to capitalize
|
|
9
|
+
* @returns {string} The capitalized string
|
|
10
|
+
*/
|
|
11
|
+
export const capitalize = (value: unknown): string => {
|
|
12
|
+
if (value == null) {
|
|
13
|
+
return ''
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const stringValue = String(value)
|
|
17
|
+
return stringValue.charAt(0).toUpperCase() + stringValue.slice(1).toLowerCase()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Convert a string into a URL-friendly "slug".
|
|
22
|
+
*
|
|
23
|
+
* @param {unknown} text The text to slugify
|
|
24
|
+
* @returns {string} The slugified string
|
|
25
|
+
*/
|
|
26
|
+
export const slugify = (text: unknown): string => {
|
|
27
|
+
if (!text) {
|
|
28
|
+
return ''
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return text
|
|
32
|
+
.toString()
|
|
33
|
+
.normalize('NFD')
|
|
34
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
35
|
+
.toLowerCase()
|
|
36
|
+
.trim()
|
|
37
|
+
.replace(/\s+/g, '-')
|
|
38
|
+
.replace(/[^\w-]+/g, '')
|
|
39
|
+
.replace(/--+/g, '-')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Limit the number of characters in a string.
|
|
44
|
+
*
|
|
45
|
+
* @param {unknown} value The string to limit
|
|
46
|
+
* @param {number} size The maximum number of characters
|
|
47
|
+
* @returns {string} The limited string
|
|
48
|
+
*/
|
|
49
|
+
export const strLimit = (value: unknown, size: number): string => {
|
|
50
|
+
if (!value) {
|
|
51
|
+
return ''
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const stringValue = String(value)
|
|
55
|
+
return stringValue.length <= size ? stringValue : `${stringValue.slice(0, size)}…`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Normalize a string by trimming, lowercasing, and removing accents.
|
|
60
|
+
*
|
|
61
|
+
* @param {string | null | undefined} value The string to normalize
|
|
62
|
+
* @returns {string | null | undefined} The normalized string
|
|
63
|
+
*/
|
|
64
|
+
export const normalizeString = (value: string | null | undefined): string | null | undefined => {
|
|
65
|
+
if (!value) {
|
|
66
|
+
return value
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return value
|
|
70
|
+
.trim()
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.normalize('NFD')
|
|
73
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// String containment helpers
|
|
77
|
+
/**
|
|
78
|
+
* Determine if a string contains all of the given values.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} haystack The string to search in
|
|
81
|
+
* @param {string[]} needles The values to search for
|
|
82
|
+
* @returns {boolean} True if all values are found
|
|
83
|
+
*/
|
|
84
|
+
export const strContainsAll = (haystack: string, needles: string[]): boolean => {
|
|
85
|
+
if (!haystack || !Array.isArray(needles)) {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
return needles.every((needle) => haystack.includes(needle))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Determine if a string contains any of the given values.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} haystack The string to search in
|
|
95
|
+
* @param {string[]} needles The values to search for
|
|
96
|
+
* @returns {boolean} True if any value is found
|
|
97
|
+
*/
|
|
98
|
+
export const strContainsAny = (haystack: string, needles: string[]): boolean => {
|
|
99
|
+
if (!haystack || !Array.isArray(needles)) {
|
|
100
|
+
return false
|
|
101
|
+
}
|
|
102
|
+
return needles.some((needle) => haystack.includes(needle))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// String padding helpers
|
|
106
|
+
/**
|
|
107
|
+
* Cap a string with a single instance of a given value.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} value The source string
|
|
110
|
+
* @param {string} cap The value to append if not present
|
|
111
|
+
* @returns {string} The capped string
|
|
112
|
+
*/
|
|
113
|
+
export const strFinish = (value: string, cap: string): string => {
|
|
114
|
+
if (!value) {
|
|
115
|
+
return cap
|
|
116
|
+
}
|
|
117
|
+
if (!cap) {
|
|
118
|
+
return value
|
|
119
|
+
}
|
|
120
|
+
return value.endsWith(cap) ? value : value + cap
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Begin a string with a single instance of a given value.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} value The source string
|
|
127
|
+
* @param {string} prefix The value to prepend if not present
|
|
128
|
+
* @returns {string} The prefixed string
|
|
129
|
+
*/
|
|
130
|
+
export const strStart = (value: string, prefix: string): string => {
|
|
131
|
+
if (!value) {
|
|
132
|
+
return prefix
|
|
133
|
+
}
|
|
134
|
+
if (!prefix) {
|
|
135
|
+
return value
|
|
136
|
+
}
|
|
137
|
+
return value.startsWith(prefix) ? value : prefix + value
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Pad both sides of a string with another.
|
|
142
|
+
*
|
|
143
|
+
* @param {string} str The source string
|
|
144
|
+
* @param {number} length The desired length
|
|
145
|
+
* @param {string} [pad=' '] The value to pad with
|
|
146
|
+
* @returns {string} The padded string
|
|
147
|
+
*/
|
|
148
|
+
export const strPadBoth = (str: string, length: number, pad = ' '): string => {
|
|
149
|
+
if (str.length >= length) {
|
|
150
|
+
return str
|
|
151
|
+
}
|
|
152
|
+
const totalPad = length - str.length
|
|
153
|
+
const leftPad = Math.floor(totalPad / 2)
|
|
154
|
+
const rightPad = totalPad - leftPad
|
|
155
|
+
return pad.repeat(leftPad) + str + pad.repeat(rightPad)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Pad the left side of a string with another.
|
|
160
|
+
*
|
|
161
|
+
* @param {string} str The source string
|
|
162
|
+
* @param {number} length The desired length
|
|
163
|
+
* @param {string} [pad=' '] The value to pad with
|
|
164
|
+
* @returns {string} The padded string
|
|
165
|
+
*/
|
|
166
|
+
export const strPadLeft = (str: string, length: number, pad = ' '): string => {
|
|
167
|
+
return str.padStart(length, pad)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Pad the right side of a string with another.
|
|
172
|
+
*
|
|
173
|
+
* @param {string} str The source string
|
|
174
|
+
* @param {number} length The desired length
|
|
175
|
+
* @param {string} [pad=' '] The value to pad with
|
|
176
|
+
* @returns {string} The padded string
|
|
177
|
+
*/
|
|
178
|
+
export const strPadRight = (str: string, length: number, pad = ' '): string => {
|
|
179
|
+
return str.padEnd(length, pad)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// String manipulation helpers
|
|
183
|
+
/**
|
|
184
|
+
* Reverse the given string.
|
|
185
|
+
*
|
|
186
|
+
* @param {string} value The string to reverse
|
|
187
|
+
* @returns {string} The reversed string
|
|
188
|
+
*/
|
|
189
|
+
export const strReverse = (value: string): string => {
|
|
190
|
+
return value.split('').reverse().join('')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Remove any occurrence of the given string in the subject.
|
|
195
|
+
*
|
|
196
|
+
* @param {string} search The string to remove
|
|
197
|
+
* @param {string} subject The source string
|
|
198
|
+
* @param {boolean} [caseSensitive=true] Whether to perform case-sensitive removal
|
|
199
|
+
* @returns {string} The resulting string
|
|
200
|
+
*/
|
|
201
|
+
export const strRemove = (search: string, subject: string, caseSensitive = true): string => {
|
|
202
|
+
if (!search || !subject) {
|
|
203
|
+
return subject
|
|
204
|
+
}
|
|
205
|
+
const flags = caseSensitive ? 'g' : 'gi'
|
|
206
|
+
return subject.replace(new RegExp(search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), flags), '')
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Mask a portion of a string with a repeated character.
|
|
211
|
+
*
|
|
212
|
+
* @param {string} str The source string
|
|
213
|
+
* @param {string} character The character to use for masking
|
|
214
|
+
* @param {number} index The starting index
|
|
215
|
+
* @param {number} [length] The number of characters to mask
|
|
216
|
+
* @returns {string} The masked string
|
|
217
|
+
*/
|
|
218
|
+
export const strMask = (str: string, character: string, index: number, length?: number): string => {
|
|
219
|
+
if (!str) {
|
|
220
|
+
return str
|
|
221
|
+
}
|
|
222
|
+
const start = Math.max(0, index)
|
|
223
|
+
const end = length !== undefined ? start + length : str.length
|
|
224
|
+
return str.substring(0, start) + character.repeat(end - start) + str.substring(end)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Word helpers
|
|
228
|
+
/**
|
|
229
|
+
* Count the number of words in a string.
|
|
230
|
+
*
|
|
231
|
+
* @param {string} str The string to count words in
|
|
232
|
+
* @param {string} [characters] Optional characters to treat as word boundaries
|
|
233
|
+
* @returns {number} The word count
|
|
234
|
+
*/
|
|
235
|
+
export const strWordCount = (str: string, characters?: string): number => {
|
|
236
|
+
if (!str) {
|
|
237
|
+
return 0
|
|
238
|
+
}
|
|
239
|
+
const pattern = characters ? new RegExp(`[${characters}\\s]+`) : /\s+/
|
|
240
|
+
return str
|
|
241
|
+
.trim()
|
|
242
|
+
.split(pattern)
|
|
243
|
+
.filter((word) => 0 < word.length).length
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Limit the number of words in a string.
|
|
248
|
+
*
|
|
249
|
+
* @param {string} value The string to limit
|
|
250
|
+
* @param {number} [words=100] The maximum number of words
|
|
251
|
+
* @param {string} [end='...'] The string to append if truncated
|
|
252
|
+
* @returns {string} The limited string
|
|
253
|
+
*/
|
|
254
|
+
export const strWords = (value: string, words = 100, end = '...'): string => {
|
|
255
|
+
if (!value) {
|
|
256
|
+
return ''
|
|
257
|
+
}
|
|
258
|
+
const wordArray = value.trim().split(/\s+/)
|
|
259
|
+
if (wordArray.length <= words) {
|
|
260
|
+
return value
|
|
261
|
+
}
|
|
262
|
+
return wordArray.slice(0, words).join(' ') + end
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Case transformation helpers
|
|
266
|
+
/**
|
|
267
|
+
* Convert a string to camelCase.
|
|
268
|
+
*
|
|
269
|
+
* @param {string} value The string to convert
|
|
270
|
+
* @returns {string} The camelCased string
|
|
271
|
+
*/
|
|
272
|
+
export const camelCase = (value: string): string => {
|
|
273
|
+
const studly = studlyCase(value)
|
|
274
|
+
return studly.charAt(0).toLowerCase() + studly.slice(1)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Convert a string to snake_case.
|
|
279
|
+
*
|
|
280
|
+
* @param {string} value The string to convert
|
|
281
|
+
* @returns {string} The snake_cased string
|
|
282
|
+
*/
|
|
283
|
+
export const snakeCase = (value: string): string => {
|
|
284
|
+
return value
|
|
285
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
286
|
+
.replace(/[\s-]+/g, '_')
|
|
287
|
+
.toLowerCase()
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Convert a string to kebab-case.
|
|
292
|
+
*
|
|
293
|
+
* @param {string} value The string to convert
|
|
294
|
+
* @returns {string} The kebab-cased string
|
|
295
|
+
*/
|
|
296
|
+
export const kebabCase = (value: string): string => {
|
|
297
|
+
return value
|
|
298
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
299
|
+
.replace(/[\s_]+/g, '-')
|
|
300
|
+
.toLowerCase()
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Convert a string to StudlyCase.
|
|
305
|
+
*
|
|
306
|
+
* @param {string} value The string to convert
|
|
307
|
+
* @returns {string} The studlyCased string
|
|
308
|
+
*/
|
|
309
|
+
export const studlyCase = (value: string): string => {
|
|
310
|
+
return value
|
|
311
|
+
.replace(/[_-]/g, ' ')
|
|
312
|
+
.replace(/\s+(.)/g, (_, c) => c.toUpperCase())
|
|
313
|
+
.replace(/^(.)/, (_, c) => c.toUpperCase())
|
|
314
|
+
.replace(/\s+/g, '')
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Convert a string to Title Case.
|
|
319
|
+
*
|
|
320
|
+
* @param {string} value The string to convert
|
|
321
|
+
* @returns {string} The titleCased string
|
|
322
|
+
*/
|
|
323
|
+
export const titleCase = (value: string): string => {
|
|
324
|
+
return value.replace(/\w\S*/g, (txt) => {
|
|
325
|
+
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Format a number with grouped thousands and decimal point.
|
|
331
|
+
*
|
|
332
|
+
* @param {number} value The number to format
|
|
333
|
+
* @param {number} [decimals=2] The number of decimal points
|
|
334
|
+
* @param {string} [locale='en-US'] The locale to use for formatting
|
|
335
|
+
* @returns {string} The formatted number
|
|
336
|
+
*/
|
|
337
|
+
export const numberFormat = (value: number, decimals: number = 2, locale: string = 'en-US'): string => {
|
|
338
|
+
return new Intl.NumberFormat(locale, {
|
|
339
|
+
minimumFractionDigits: decimals,
|
|
340
|
+
maximumFractionDigits: decimals,
|
|
341
|
+
}).format(value)
|
|
342
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zairakai/npm-helpers
|
|
3
|
+
* Collection of JavaScript utility functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Export all validators
|
|
7
|
+
export * from './validators.js'
|
|
8
|
+
|
|
9
|
+
// Export all formatters
|
|
10
|
+
export * from './formatters.js'
|
|
11
|
+
|
|
12
|
+
// Export all array helpers
|
|
13
|
+
export * from './arrays.js'
|
|
14
|
+
|
|
15
|
+
// Export enhanced collections
|
|
16
|
+
export * from './collections.js'
|
|
17
|
+
|
|
18
|
+
// Export PHP-like array functions
|
|
19
|
+
export * from './php-arrays.js'
|
|
20
|
+
|
|
21
|
+
// Export datetime utilities
|
|
22
|
+
export * from './datetime.js'
|
|
23
|
+
|
|
24
|
+
// Export runtime validation schemas
|
|
25
|
+
export * from './schemas.js'
|
|
26
|
+
|
|
27
|
+
// Export new runtime helpers
|
|
28
|
+
export * from './equals.js'
|
|
29
|
+
export * from './number.js'
|
|
30
|
+
export * from './obj.js'
|
|
31
|
+
export * from './runtime.js'
|
|
32
|
+
export * from './str.js'
|
|
33
|
+
export * from './types.js'
|
|
34
|
+
export * from './validator.js'
|
|
35
|
+
|
|
36
|
+
export * from './pipe.js'
|