@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.
Files changed (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +270 -0
  3. package/dist/arrays.cjs +210 -0
  4. package/dist/arrays.d.cts +119 -0
  5. package/dist/arrays.d.ts +119 -0
  6. package/dist/arrays.js +32 -0
  7. package/dist/chunk-27YHP2CK.js +407 -0
  8. package/dist/chunk-3WNRYKPG.js +37 -0
  9. package/dist/chunk-42CHLXT7.js +214 -0
  10. package/dist/chunk-6F4PWJZI.js +0 -0
  11. package/dist/chunk-7SXRFZBB.js +173 -0
  12. package/dist/chunk-F6RSTW65.js +156 -0
  13. package/dist/chunk-G7ZJ23DW.js +253 -0
  14. package/dist/chunk-IPP7PA6H.js +136 -0
  15. package/dist/chunk-LDSWHSRX.js +96 -0
  16. package/dist/chunk-TY75OOIQ.js +700 -0
  17. package/dist/chunk-W6JEMFAF.js +54 -0
  18. package/dist/chunk-XEJLBAXE.js +164 -0
  19. package/dist/chunk-Z7G3SIQH.js +270 -0
  20. package/dist/chunk-ZJPKS2MQ.js +101 -0
  21. package/dist/collections.cjs +797 -0
  22. package/dist/collections.d.cts +353 -0
  23. package/dist/collections.d.ts +353 -0
  24. package/dist/collections.js +17 -0
  25. package/dist/datetime.cjs +80 -0
  26. package/dist/datetime.d.cts +75 -0
  27. package/dist/datetime.d.ts +75 -0
  28. package/dist/datetime.js +24 -0
  29. package/dist/equals.cjs +121 -0
  30. package/dist/equals.d.cts +24 -0
  31. package/dist/equals.d.ts +24 -0
  32. package/dist/equals.js +8 -0
  33. package/dist/formatters.cjs +201 -0
  34. package/dist/formatters.d.cts +180 -0
  35. package/dist/formatters.d.ts +180 -0
  36. package/dist/formatters.js +48 -0
  37. package/dist/index.cjs +2906 -0
  38. package/dist/index.d.cts +120 -0
  39. package/dist/index.d.ts +120 -0
  40. package/dist/index.js +348 -0
  41. package/dist/number.cjs +279 -0
  42. package/dist/number.d.cts +177 -0
  43. package/dist/number.d.ts +177 -0
  44. package/dist/number.js +10 -0
  45. package/dist/obj.cjs +427 -0
  46. package/dist/obj.d.cts +177 -0
  47. package/dist/obj.d.ts +177 -0
  48. package/dist/obj.js +12 -0
  49. package/dist/php-arrays.cjs +954 -0
  50. package/dist/php-arrays.d.cts +256 -0
  51. package/dist/php-arrays.d.ts +256 -0
  52. package/dist/php-arrays.js +70 -0
  53. package/dist/runtime.cjs +134 -0
  54. package/dist/runtime.d.cts +90 -0
  55. package/dist/runtime.d.ts +90 -0
  56. package/dist/runtime.js +24 -0
  57. package/dist/schemas.cjs +86 -0
  58. package/dist/schemas.d.cts +108 -0
  59. package/dist/schemas.d.ts +108 -0
  60. package/dist/schemas.js +22 -0
  61. package/dist/str.cjs +499 -0
  62. package/dist/str.d.cts +282 -0
  63. package/dist/str.d.ts +282 -0
  64. package/dist/str.js +11 -0
  65. package/dist/types.cjs +18 -0
  66. package/dist/types.d.cts +13 -0
  67. package/dist/types.d.ts +13 -0
  68. package/dist/types.js +1 -0
  69. package/dist/validator.cjs +251 -0
  70. package/dist/validator.d.cts +99 -0
  71. package/dist/validator.d.ts +99 -0
  72. package/dist/validator.js +11 -0
  73. package/dist/validators.cjs +217 -0
  74. package/dist/validators.d.cts +216 -0
  75. package/dist/validators.d.ts +216 -0
  76. package/dist/validators.js +64 -0
  77. package/package.json +180 -0
  78. package/src/arrays.ts +316 -0
  79. package/src/collections.ts +866 -0
  80. package/src/datetime.ts +103 -0
  81. package/src/equals.ts +134 -0
  82. package/src/formatters.ts +342 -0
  83. package/src/index.ts +36 -0
  84. package/src/number.ts +281 -0
  85. package/src/obj.ts +303 -0
  86. package/src/php-arrays.ts +445 -0
  87. package/src/pipe.ts +29 -0
  88. package/src/runtime.ts +194 -0
  89. package/src/schemas.ts +136 -0
  90. package/src/str.ts +438 -0
  91. package/src/types.ts +13 -0
  92. package/src/validator.ts +157 -0
  93. package/src/validators.ts +359 -0
@@ -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
+ }