@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/validator.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { diff, isEqual } from './equals.js'
|
|
3
|
+
import { GenericRecord } from './types.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validator class inspired by Laravel's Validator facade
|
|
7
|
+
* Uses Zod under the hood for robust runtime validation
|
|
8
|
+
*/
|
|
9
|
+
export class Validator {
|
|
10
|
+
/**
|
|
11
|
+
* Create a new validation instance
|
|
12
|
+
*
|
|
13
|
+
* @param {unknown} data The data to validate
|
|
14
|
+
* @param {T | z.ZodObject<T>} rules The validation rules (Zod shape or object)
|
|
15
|
+
* @returns {ValidationInstance<z.ZodObject<T>>} A new ValidationInstance
|
|
16
|
+
*/
|
|
17
|
+
static make<T extends z.ZodRawShape>(data: unknown, rules: T | z.ZodObject<T>) {
|
|
18
|
+
const schema = rules instanceof z.ZodObject ? rules : z.object(rules)
|
|
19
|
+
return new ValidationInstance(data, schema)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validate data against rules and throw if it fails
|
|
24
|
+
*
|
|
25
|
+
* @param {unknown} data The data to validate
|
|
26
|
+
* @param {T | z.ZodObject<T>} rules The validation rules (Zod shape or object)
|
|
27
|
+
* @returns {z.infer<z.ZodObject<T>>} The validated data
|
|
28
|
+
* @throws {z.ZodError} If validation fails
|
|
29
|
+
*/
|
|
30
|
+
static validate<T extends z.ZodRawShape>(data: unknown, rules: T | z.ZodObject<T>) {
|
|
31
|
+
const schema = rules instanceof z.ZodObject ? rules : z.object(rules)
|
|
32
|
+
return schema.parse(data)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Compare two objects for equality
|
|
37
|
+
*
|
|
38
|
+
* @param {unknown} a First object
|
|
39
|
+
* @param {unknown} b Second object
|
|
40
|
+
* @returns {boolean} True if the objects are equal
|
|
41
|
+
*/
|
|
42
|
+
static isEqual(a: unknown, b: unknown): boolean {
|
|
43
|
+
return isEqual(a, b)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get the differences between two objects
|
|
48
|
+
*
|
|
49
|
+
* @param {unknown} a First object
|
|
50
|
+
* @param {unknown} b Second object
|
|
51
|
+
* @returns {GenericRecord} An object containing the differences
|
|
52
|
+
*/
|
|
53
|
+
static diff(a: unknown, b: unknown): GenericRecord {
|
|
54
|
+
return diff(a, b)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Individual validation instance
|
|
60
|
+
*/
|
|
61
|
+
export class ValidationInstance<T extends z.ZodTypeAny> {
|
|
62
|
+
private result: ReturnType<T['safeParse']>
|
|
63
|
+
|
|
64
|
+
constructor(
|
|
65
|
+
private data: unknown,
|
|
66
|
+
private schema: T
|
|
67
|
+
) {
|
|
68
|
+
this.result = this.schema.safeParse(this.data) as ReturnType<T['safeParse']>
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if validation failed
|
|
73
|
+
*
|
|
74
|
+
* @returns {boolean} True if validation failed
|
|
75
|
+
*/
|
|
76
|
+
fails(): boolean {
|
|
77
|
+
return !this.result.success
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if validation passed
|
|
82
|
+
*
|
|
83
|
+
* @returns {boolean} True if validation passed
|
|
84
|
+
*/
|
|
85
|
+
passes(): boolean {
|
|
86
|
+
return this.result.success
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get validation errors
|
|
91
|
+
*
|
|
92
|
+
* @returns {Record<string, string[]>} An object with field names as keys and arrays of error messages as values
|
|
93
|
+
*/
|
|
94
|
+
errors(): Record<string, string[]> {
|
|
95
|
+
if (this.result.success) {
|
|
96
|
+
return {}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const errors: Record<string, string[]> = {}
|
|
100
|
+
this.result.error.issues.forEach((err: z.ZodIssue) => {
|
|
101
|
+
const path = err.path.join('.') || 'root'
|
|
102
|
+
if (!errors[path]) {
|
|
103
|
+
errors[path] = []
|
|
104
|
+
}
|
|
105
|
+
errors[path].push(err.message)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
return errors
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get the first error message for a given field
|
|
113
|
+
*
|
|
114
|
+
* @param {string} field The field name
|
|
115
|
+
* @returns {string | undefined} The first error message or undefined
|
|
116
|
+
*/
|
|
117
|
+
firstError(field: string): string | undefined {
|
|
118
|
+
const fieldErrors = this.errors()[field]
|
|
119
|
+
return fieldErrors ? fieldErrors[0] : undefined
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the validated data
|
|
124
|
+
*
|
|
125
|
+
* @returns {z.infer<T>} The validated data
|
|
126
|
+
* @throws {Error} If validation failed
|
|
127
|
+
*/
|
|
128
|
+
validated(): z.infer<T> {
|
|
129
|
+
if (!this.result.success) {
|
|
130
|
+
throw new Error('Validation failed')
|
|
131
|
+
}
|
|
132
|
+
return this.result.data as z.infer<T>
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get all error messages as a flat array
|
|
137
|
+
*
|
|
138
|
+
* @returns {string[]} An array of all error messages
|
|
139
|
+
*/
|
|
140
|
+
allErrors(): string[] {
|
|
141
|
+
if (this.result.success) {
|
|
142
|
+
return []
|
|
143
|
+
}
|
|
144
|
+
return this.result.error.issues.map((err: z.ZodIssue) => `${err.path.join('.')}: ${err.message}`)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Helper function to create a validator
|
|
150
|
+
*
|
|
151
|
+
* @param {unknown} data The data to validate
|
|
152
|
+
* @param {T | z.ZodObject<T>} rules The validation rules
|
|
153
|
+
* @returns {ValidationInstance<z.ZodObject<T>>} A new ValidationInstance
|
|
154
|
+
*/
|
|
155
|
+
export const validator = <T extends z.ZodRawShape>(data: unknown, rules: T | z.ZodObject<T>) => {
|
|
156
|
+
return Validator.make(data, rules)
|
|
157
|
+
}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type checking and validation utilities
|
|
3
|
+
* Laravel-inspired validation helpers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if value is strictly true
|
|
8
|
+
*
|
|
9
|
+
* @param {unknown} value The value to check
|
|
10
|
+
* @returns {value is true} True if the value is strictly true
|
|
11
|
+
*/
|
|
12
|
+
export const isTrue = (value: unknown): value is true => true === value
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check if value is strictly false
|
|
16
|
+
*
|
|
17
|
+
* @param {unknown} value The value to check
|
|
18
|
+
* @returns {value is false} True if the value is strictly false
|
|
19
|
+
*/
|
|
20
|
+
export const isFalse = (value: unknown): value is false => false === value
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if value is strictly null
|
|
24
|
+
*
|
|
25
|
+
* @param {unknown} value The value to check
|
|
26
|
+
* @returns {value is null} True if the value is strictly null
|
|
27
|
+
*/
|
|
28
|
+
export const isNull = (value: unknown): value is null => null === value
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if value is strictly undefined
|
|
32
|
+
*
|
|
33
|
+
* @param {unknown} value The value to check
|
|
34
|
+
* @returns {value is undefined} True if the value is strictly undefined
|
|
35
|
+
*/
|
|
36
|
+
export const isUndefined = (value: unknown): value is undefined => undefined === value
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if value is neither null nor undefined
|
|
40
|
+
*
|
|
41
|
+
* @param {unknown} value The value to check
|
|
42
|
+
* @returns {value is NonNullable<unknown>} True if the value is set
|
|
43
|
+
*/
|
|
44
|
+
export const isSet = (value: unknown): value is NonNullable<unknown> => undefined !== value && null !== value
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if value is an array
|
|
48
|
+
*
|
|
49
|
+
* @param {unknown} value The value to check
|
|
50
|
+
* @returns {value is unknown[]} True if the value is an array
|
|
51
|
+
*/
|
|
52
|
+
export const isArray = (value: unknown): value is unknown[] => Array.isArray(value)
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if value is a plain object (Record)
|
|
56
|
+
*
|
|
57
|
+
* @param {unknown} value The value to check
|
|
58
|
+
* @returns {value is Record<string, unknown>} True if the value is a plain object
|
|
59
|
+
*/
|
|
60
|
+
export const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
61
|
+
'object' === typeof value && null !== value && !Array.isArray(value)
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if value is a string
|
|
65
|
+
*
|
|
66
|
+
* @param {unknown} value The value to check
|
|
67
|
+
* @returns {value is string} True if the value is a string
|
|
68
|
+
*/
|
|
69
|
+
export const isString = (value: unknown): value is string => 'string' === typeof value
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if value is a number and not NaN
|
|
73
|
+
*
|
|
74
|
+
* @param {unknown} value The value to check
|
|
75
|
+
* @returns {value is number} True if the value is a valid number
|
|
76
|
+
*/
|
|
77
|
+
export const isNumber = (value: unknown): value is number => 'number' === typeof value && !isNaN(value)
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if value is an integer
|
|
81
|
+
*
|
|
82
|
+
* @param {unknown} value The value to check
|
|
83
|
+
* @returns {value is number} True if the value is an integer
|
|
84
|
+
*/
|
|
85
|
+
export const isInteger = (value: unknown): value is number => Number.isInteger(value)
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if value is a float
|
|
89
|
+
*
|
|
90
|
+
* @param {unknown} value The value to check
|
|
91
|
+
* @returns {value is number} True if the value is a float
|
|
92
|
+
*/
|
|
93
|
+
export const isFloat = (value: unknown): value is number =>
|
|
94
|
+
'number' === typeof value && !isNaN(value) && !Number.isInteger(value)
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if value is a boolean
|
|
98
|
+
*
|
|
99
|
+
* @param {unknown} value The value to check
|
|
100
|
+
* @returns {value is boolean} True if the value is a boolean
|
|
101
|
+
*/
|
|
102
|
+
export const isBoolean = (value: unknown): value is boolean => 'boolean' === typeof value
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if value is a function
|
|
106
|
+
*
|
|
107
|
+
* @param {unknown} value The value to check
|
|
108
|
+
* @returns {value is Function} True if the value is a function
|
|
109
|
+
*/
|
|
110
|
+
export const isFunction = (value: unknown): value is Function => 'function' === typeof value
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if value is a valid Date object
|
|
114
|
+
*
|
|
115
|
+
* @param {unknown} value The value to check
|
|
116
|
+
* @returns {value is Date} True if the value is a valid Date object
|
|
117
|
+
*/
|
|
118
|
+
export const isDate = (value: unknown): value is Date => value instanceof Date && !isNaN(value.getTime())
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check if value is numeric (number or numeric string)
|
|
122
|
+
*
|
|
123
|
+
* @param {unknown} value The value to check
|
|
124
|
+
* @returns {value is number | string} True if the value is numeric
|
|
125
|
+
*/
|
|
126
|
+
export const isNumeric = (value: unknown): value is number | string => {
|
|
127
|
+
if ('number' === typeof value) {
|
|
128
|
+
return !isNaN(value) && isFinite(value)
|
|
129
|
+
}
|
|
130
|
+
if ('string' === typeof value) {
|
|
131
|
+
const num = Number(value.trim())
|
|
132
|
+
return '' !== value.trim() && !isNaN(num) && isFinite(num)
|
|
133
|
+
}
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Check if value is a valid email address format
|
|
139
|
+
*
|
|
140
|
+
* @param {unknown} value The value to check
|
|
141
|
+
* @returns {value is string} True if the value is a valid email string
|
|
142
|
+
*/
|
|
143
|
+
export const isEmail = (value: unknown): value is string => {
|
|
144
|
+
if ('string' !== typeof value) {
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
148
|
+
return emailRegex.test(value)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if value is a valid URL
|
|
153
|
+
*
|
|
154
|
+
* @param {unknown} value The value to check
|
|
155
|
+
* @returns {value is string} True if the value is a valid URL string
|
|
156
|
+
*/
|
|
157
|
+
export const isUrl = (value: unknown): value is string => {
|
|
158
|
+
if ('string' !== typeof value) {
|
|
159
|
+
return false
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
new URL(value)
|
|
163
|
+
return true
|
|
164
|
+
} catch {
|
|
165
|
+
return false
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if value is empty (null, undefined, empty string/array/object, zero, or false)
|
|
171
|
+
*
|
|
172
|
+
* @param {unknown} value The value to check
|
|
173
|
+
* @returns {boolean} True if the value is empty
|
|
174
|
+
*/
|
|
175
|
+
export const isEmpty = (value: unknown): boolean => {
|
|
176
|
+
if (isNull(value) || isUndefined(value)) {
|
|
177
|
+
return true
|
|
178
|
+
}
|
|
179
|
+
if (isString(value)) {
|
|
180
|
+
return 0 === value.trim().length
|
|
181
|
+
}
|
|
182
|
+
if (isArray(value)) {
|
|
183
|
+
return 0 === value.length
|
|
184
|
+
}
|
|
185
|
+
if (isObject(value)) {
|
|
186
|
+
return 0 === Object.keys(value).length
|
|
187
|
+
}
|
|
188
|
+
if (isNumber(value)) {
|
|
189
|
+
return 0 === value
|
|
190
|
+
}
|
|
191
|
+
if (isBoolean(value)) {
|
|
192
|
+
return isFalse(value)
|
|
193
|
+
}
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Check if value is not empty
|
|
199
|
+
*
|
|
200
|
+
* @param {unknown} value The value to check
|
|
201
|
+
* @returns {boolean} True if the value is not empty
|
|
202
|
+
*/
|
|
203
|
+
export const isNotEmpty = (value: unknown): boolean => !isEmpty(value)
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Check if value is blank (null, undefined, or empty string/whitespace)
|
|
207
|
+
*
|
|
208
|
+
* @param {unknown} value The value to check
|
|
209
|
+
* @returns {boolean} True if the value is blank
|
|
210
|
+
*/
|
|
211
|
+
export const isBlank = (value: unknown): boolean => {
|
|
212
|
+
if (value == null) {
|
|
213
|
+
return true
|
|
214
|
+
}
|
|
215
|
+
if ('string' === typeof value) {
|
|
216
|
+
return '' === value.trim()
|
|
217
|
+
}
|
|
218
|
+
return isEmpty(value)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Check if value is present (not blank)
|
|
223
|
+
*
|
|
224
|
+
* @param {unknown} value The value to check
|
|
225
|
+
* @returns {boolean} True if the value is present
|
|
226
|
+
*/
|
|
227
|
+
export const isPresent = (value: unknown): boolean => !isBlank(value)
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Alias for isPresent()
|
|
231
|
+
*
|
|
232
|
+
* @param {unknown} value The value to check
|
|
233
|
+
* @returns {boolean} True if the value is filled
|
|
234
|
+
*/
|
|
235
|
+
export const filled = (value: unknown): boolean => isPresent(value)
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Alias for isBlank()
|
|
239
|
+
*
|
|
240
|
+
* @param {unknown} value The value to check
|
|
241
|
+
* @returns {boolean} True if the value is blank
|
|
242
|
+
*/
|
|
243
|
+
export const blank = (value: unknown): boolean => isBlank(value)
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Check if value is an even number
|
|
247
|
+
*
|
|
248
|
+
* @param {unknown} value The value to check
|
|
249
|
+
* @returns {value is number} True if the value is an even number
|
|
250
|
+
*/
|
|
251
|
+
export const isEven = (value: unknown): value is number => {
|
|
252
|
+
return isNumber(value) && 0 === value % 2
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Check if value is an odd number
|
|
257
|
+
*
|
|
258
|
+
* @param {unknown} value The value to check
|
|
259
|
+
* @returns {value is number} True if the value is an odd number
|
|
260
|
+
*/
|
|
261
|
+
export const isOdd = (value: unknown): value is number => {
|
|
262
|
+
return isNumber(value) && 0 !== value % 2
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Check if value is a valid JSON string
|
|
267
|
+
*
|
|
268
|
+
* @param {unknown} value The value to check
|
|
269
|
+
* @returns {boolean} True if the value is a valid JSON string
|
|
270
|
+
*/
|
|
271
|
+
export const isJson = (value: unknown): boolean => {
|
|
272
|
+
if ('string' !== typeof value) {
|
|
273
|
+
return false
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
JSON.parse(value)
|
|
278
|
+
return true
|
|
279
|
+
} catch {
|
|
280
|
+
return false
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Check if value is a valid Base64 encoded string
|
|
286
|
+
*
|
|
287
|
+
* @param {unknown} value The value to check
|
|
288
|
+
* @returns {boolean} True if the value is a valid Base64 string
|
|
289
|
+
*/
|
|
290
|
+
export const isBase64 = (value: unknown): boolean => {
|
|
291
|
+
if ('string' !== typeof value) {
|
|
292
|
+
return false
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Base64 pattern: groups of 4 characters from the base64 alphabet
|
|
296
|
+
const base64Pattern = /^[A-Za-z0-9+/]*={0,2}$/
|
|
297
|
+
|
|
298
|
+
// Check if length is multiple of 4
|
|
299
|
+
if (0 !== value.length % 4) {
|
|
300
|
+
return false
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return base64Pattern.test(value)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Check if value is a valid MAC address
|
|
308
|
+
*
|
|
309
|
+
* @param {unknown} value The value to check
|
|
310
|
+
* @returns {boolean} True if the value is a valid MAC address
|
|
311
|
+
*/
|
|
312
|
+
export const isMacAddress = (value: unknown): boolean => {
|
|
313
|
+
if ('string' !== typeof value) {
|
|
314
|
+
return false
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// MAC address patterns: XX:XX:XX:XX:XX:XX or XX-XX-XX-XX-XX-XX
|
|
318
|
+
const macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/
|
|
319
|
+
return macPattern.test(value)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Check if value is a valid UUID (v4)
|
|
324
|
+
*
|
|
325
|
+
* @param {unknown} value The value to check
|
|
326
|
+
* @returns {value is string} True if the value is a valid UUID v4 string
|
|
327
|
+
*/
|
|
328
|
+
export const isUuid = (value: unknown): value is string => {
|
|
329
|
+
if ('string' !== typeof value) {
|
|
330
|
+
return false
|
|
331
|
+
}
|
|
332
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Check if value is a valid IPv4 or IPv6 address
|
|
337
|
+
*
|
|
338
|
+
* @param {unknown} value The value to check
|
|
339
|
+
* @returns {value is string} True if the value is a valid IP address string
|
|
340
|
+
*/
|
|
341
|
+
export const isIp = (value: unknown): value is string => {
|
|
342
|
+
if ('string' !== typeof value) {
|
|
343
|
+
return false
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// IPv4
|
|
347
|
+
const ipv4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
|
|
348
|
+
if (ipv4.test(value)) {
|
|
349
|
+
return value.split('.').every((part) => {
|
|
350
|
+
const n = Number(part)
|
|
351
|
+
return n >= 0 && n <= 255 && String(n) === part
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// IPv6 (covers full, compressed, and loopback forms)
|
|
356
|
+
const ipv6 =
|
|
357
|
+
/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|(([0-9a-fA-F]{1,4}:){0,7}:)|(:[0-9a-fA-F]{1,4}){1,7}|(([0-9a-fA-F]{1,4}:){1,7}:)|(([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4})|::1)$/
|
|
358
|
+
return ipv6.test(value)
|
|
359
|
+
}
|