@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
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHP-like array functions for JavaScript
|
|
3
|
+
* Provides exact API compatibility between PHP arrays and JavaScript arrays
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { EnhancedArray } from './collections.js'
|
|
7
|
+
|
|
8
|
+
// PHP array function equivalents
|
|
9
|
+
/**
|
|
10
|
+
* Split an array into chunks.
|
|
11
|
+
*
|
|
12
|
+
* @param {T[]} array The array to chunk
|
|
13
|
+
* @param {number} size The size of each chunk
|
|
14
|
+
* @returns {T[][]} An array of chunks
|
|
15
|
+
*/
|
|
16
|
+
export const array_chunk = <T>(array: T[], size: number): T[][] => {
|
|
17
|
+
if (0 >= size) {
|
|
18
|
+
return []
|
|
19
|
+
}
|
|
20
|
+
const chunks: T[][] = []
|
|
21
|
+
for (let i = 0; i < array.length; i += size) {
|
|
22
|
+
chunks.push(array.slice(i, i + size))
|
|
23
|
+
}
|
|
24
|
+
return chunks
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Filter elements of an array using a callback function.
|
|
29
|
+
*
|
|
30
|
+
* @param {T[]} array The array to filter
|
|
31
|
+
* @param {Function} [callback] The callback function to use
|
|
32
|
+
* @returns {T[]} The filtered array
|
|
33
|
+
*/
|
|
34
|
+
export const array_filter = <T>(array: T[], callback?: (value: T, index: number) => boolean): T[] => {
|
|
35
|
+
if (!callback) {
|
|
36
|
+
return array.filter((item) => Boolean(item))
|
|
37
|
+
}
|
|
38
|
+
return array.filter(callback)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Apply a callback to the elements of the given arrays.
|
|
43
|
+
*
|
|
44
|
+
* @param {Function} callback The callback function to apply
|
|
45
|
+
* @param {T[]} array The array to map over
|
|
46
|
+
* @returns {U[]} The mapped array
|
|
47
|
+
*/
|
|
48
|
+
export const array_map = <T, U>(callback: (value: T, index: number) => U, array: T[]): U[] => {
|
|
49
|
+
return array.map(callback)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Iteratively reduce the array to a single value using a callback function.
|
|
54
|
+
*
|
|
55
|
+
* @param {T[]} array The source array
|
|
56
|
+
* @param {Function} callback The callback function
|
|
57
|
+
* @param {U} initial The initial value
|
|
58
|
+
* @returns {U} The reduced value
|
|
59
|
+
*/
|
|
60
|
+
export const array_reduce = <T, U>(array: T[], callback: (carry: U, item: T, index: number) => U, initial: U): U => {
|
|
61
|
+
return array.reduce((carry, item, index) => callback(carry, item, index), initial)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Merge one or more arrays.
|
|
66
|
+
*
|
|
67
|
+
* @param {...T[][]} arrays The arrays to merge
|
|
68
|
+
* @returns {T[]} The merged array
|
|
69
|
+
*/
|
|
70
|
+
export const array_merge = <T>(...arrays: T[][]): T[] => {
|
|
71
|
+
return arrays.flat()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Remove duplicate values from an array.
|
|
76
|
+
*
|
|
77
|
+
* @param {T[]} array The source array
|
|
78
|
+
* @returns {T[]} The array with unique values
|
|
79
|
+
*/
|
|
80
|
+
export const array_unique = <T>(array: T[]): T[] => {
|
|
81
|
+
return [...new Set(array)]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Return an array with elements in reverse order.
|
|
86
|
+
*
|
|
87
|
+
* @param {T[]} array The source array
|
|
88
|
+
* @param {boolean} [_preserveKeys=false] Whether to preserve keys (ignored for JS arrays)
|
|
89
|
+
* @returns {T[]} The reversed array
|
|
90
|
+
*/
|
|
91
|
+
export const array_reverse = <T>(array: T[], _preserveKeys: boolean = false): T[] => {
|
|
92
|
+
return [...array].reverse()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Extract a slice of the array.
|
|
97
|
+
*
|
|
98
|
+
* @param {T[]} array The source array
|
|
99
|
+
* @param {number} offset The starting offset
|
|
100
|
+
* @param {number} [length] The length of the slice
|
|
101
|
+
* @returns {T[]} The sliced portion of the array
|
|
102
|
+
*/
|
|
103
|
+
export const array_slice = <T>(array: T[], offset: number, length?: number): T[] => {
|
|
104
|
+
const start = 0 > offset ? Math.max(0, array.length + offset) : offset
|
|
105
|
+
const end = length !== undefined ? start + length : undefined
|
|
106
|
+
return array.slice(start, end)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Remove a portion of the array and replace it with something else.
|
|
111
|
+
*
|
|
112
|
+
* @param {T[]} array The source array
|
|
113
|
+
* @param {number} offset The starting offset
|
|
114
|
+
* @param {number} [length] The number of elements to remove
|
|
115
|
+
* @param {...T[]} replacement The elements to replace with
|
|
116
|
+
* @returns {T[]} The modified array (copy)
|
|
117
|
+
*/
|
|
118
|
+
export const array_splice = <T>(array: T[], offset: number, length?: number, ...replacement: T[]): T[] => {
|
|
119
|
+
const result = [...array]
|
|
120
|
+
const actualLength = length ?? result.length - offset
|
|
121
|
+
result.splice(offset, actualLength, ...replacement)
|
|
122
|
+
return result
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Return all the keys or a subset of the keys of an array.
|
|
127
|
+
*
|
|
128
|
+
* @param {T[]} array The source array
|
|
129
|
+
* @returns {number[]} The array keys (indices)
|
|
130
|
+
*/
|
|
131
|
+
export const array_keys = <T>(array: T[]): number[] => {
|
|
132
|
+
return array.map((_, index) => index)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Search an array for a given value and return the corresponding key if successful.
|
|
137
|
+
*
|
|
138
|
+
* @param {T} needle The value to search for
|
|
139
|
+
* @param {T[]} haystack The array to search in
|
|
140
|
+
* @returns {number | false} The key if found, false otherwise
|
|
141
|
+
*/
|
|
142
|
+
export const array_search = <T>(needle: T, haystack: T[]): number | false => {
|
|
143
|
+
const index = haystack.indexOf(needle)
|
|
144
|
+
return -1 !== index ? index : false
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Check if the given key or index exists in the array.
|
|
149
|
+
*
|
|
150
|
+
* @param {number} key The key to check
|
|
151
|
+
* @param {T[]} array The source array
|
|
152
|
+
* @returns {boolean} True if the key exists
|
|
153
|
+
*/
|
|
154
|
+
export const array_key_exists = <T>(key: number, array: T[]): boolean => {
|
|
155
|
+
return 0 <= key && key < array.length
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Pop the element off the end of array.
|
|
160
|
+
*
|
|
161
|
+
* @param {T[]} array The source array
|
|
162
|
+
* @returns {T | undefined} The popped element
|
|
163
|
+
*/
|
|
164
|
+
export const array_pop = <T>(array: T[]): T | undefined => {
|
|
165
|
+
const result = [...array]
|
|
166
|
+
return result.pop()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Push one or more elements onto the end of array.
|
|
171
|
+
*
|
|
172
|
+
* @param {T[]} array The source array
|
|
173
|
+
* @param {...T[]} values The values to push
|
|
174
|
+
* @returns {T[]} The modified array (copy)
|
|
175
|
+
*/
|
|
176
|
+
export const array_push = <T>(array: T[], ...values: T[]): T[] => {
|
|
177
|
+
return [...array, ...values]
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Shift an element off the beginning of array.
|
|
182
|
+
*
|
|
183
|
+
* @param {T[]} array The source array
|
|
184
|
+
* @returns {T | undefined} The shifted element
|
|
185
|
+
*/
|
|
186
|
+
export const array_shift = <T>(array: T[]): T | undefined => {
|
|
187
|
+
const result = [...array]
|
|
188
|
+
return result.shift()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Prepend one or more elements to the beginning of an array.
|
|
193
|
+
*
|
|
194
|
+
* @param {T[]} array The source array
|
|
195
|
+
* @param {...T[]} values The values to prepend
|
|
196
|
+
* @returns {T[]} The modified array (copy)
|
|
197
|
+
*/
|
|
198
|
+
export const array_unshift = <T>(array: T[], ...values: T[]): T[] => {
|
|
199
|
+
return [...values, ...array]
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Calculate the sum of values in an array.
|
|
204
|
+
*
|
|
205
|
+
* @param {number[]} array The source array
|
|
206
|
+
* @returns {number} The sum of values
|
|
207
|
+
*/
|
|
208
|
+
export const array_sum = (array: number[]): number => {
|
|
209
|
+
return array.reduce((sum, num) => sum + num, 0)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Calculate the product of values in an array.
|
|
214
|
+
*
|
|
215
|
+
* @param {number[]} array The source array
|
|
216
|
+
* @returns {number} The product of values
|
|
217
|
+
*/
|
|
218
|
+
export const array_product = (array: number[]): number => {
|
|
219
|
+
return array.reduce((product, num) => product * num, 1)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Pick one or more random entries out of an array.
|
|
224
|
+
*
|
|
225
|
+
* @param {T[]} array The source array
|
|
226
|
+
* @param {number} [num=1] The number of entries to pick
|
|
227
|
+
* @returns {T | T[] | undefined} The random entry or entries
|
|
228
|
+
*/
|
|
229
|
+
export const array_rand = <T>(array: T[], num: number = 1): T | T[] | undefined => {
|
|
230
|
+
if (0 === array.length) {
|
|
231
|
+
return 1 === num ? undefined : []
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const shuffled = [...array].sort(() => Math.random() - 0.5)
|
|
235
|
+
|
|
236
|
+
if (1 === num) {
|
|
237
|
+
return shuffled[0]
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return shuffled.slice(0, Math.min(num, array.length))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Exchange all keys with their associated values in an array.
|
|
245
|
+
*
|
|
246
|
+
* @param {T[]} array The source array
|
|
247
|
+
* @returns {Record<string, number>} The flipped record
|
|
248
|
+
*/
|
|
249
|
+
export const array_flip = <T extends string | number>(array: T[]): Record<string, number> => {
|
|
250
|
+
const result: Record<string, number> = {}
|
|
251
|
+
array.forEach((value, index) => {
|
|
252
|
+
result[String(value)] = index
|
|
253
|
+
})
|
|
254
|
+
return result
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Count all the values of an array.
|
|
259
|
+
*
|
|
260
|
+
* @param {T[]} array The source array
|
|
261
|
+
* @returns {Record<string, number>} A record of values and their counts
|
|
262
|
+
*/
|
|
263
|
+
export const array_count_values = <T extends string | number>(array: T[]): Record<string, number> => {
|
|
264
|
+
const result: Record<string, number> = {}
|
|
265
|
+
array.forEach((value) => {
|
|
266
|
+
const key = String(value)
|
|
267
|
+
result[key] = (result[key] || 0) + 1
|
|
268
|
+
})
|
|
269
|
+
return result
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Advanced PHP array functions
|
|
273
|
+
/**
|
|
274
|
+
* Compute the intersection of arrays.
|
|
275
|
+
*
|
|
276
|
+
* @param {...T[][]} arrays The arrays to intersect
|
|
277
|
+
* @returns {T[]} The intersection of the arrays
|
|
278
|
+
*/
|
|
279
|
+
export const array_intersect = <T>(...arrays: T[][]): T[] => {
|
|
280
|
+
if (0 === arrays.length) {
|
|
281
|
+
return []
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const first = arrays[0]
|
|
285
|
+
return first.filter((item) => arrays.slice(1).every((arr) => arr.includes(item)))
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Compute the difference of arrays.
|
|
290
|
+
*
|
|
291
|
+
* @param {T[]} array1 The array to compare from
|
|
292
|
+
* @param {...T[][]} arrays The arrays to compare against
|
|
293
|
+
* @returns {T[]} The difference of the arrays
|
|
294
|
+
*/
|
|
295
|
+
export const array_diff = <T>(array1: T[], ...arrays: T[][]): T[] => {
|
|
296
|
+
const otherItems = new Set(arrays.flat())
|
|
297
|
+
return array1.filter((item) => !otherItems.has(item))
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Return the values from a single column in the input array.
|
|
302
|
+
*
|
|
303
|
+
* @param {T[]} array The source array of objects
|
|
304
|
+
* @param {K} column The column to retrieve
|
|
305
|
+
* @returns {T[K][]} The array of column values
|
|
306
|
+
*/
|
|
307
|
+
export const array_column = <T, K extends keyof T>(array: T[], column: K): T[K][] => {
|
|
308
|
+
return array
|
|
309
|
+
.map((item) => (item && 'object' === typeof item ? item[column] : undefined))
|
|
310
|
+
.filter((value) => value !== undefined) as T[K][]
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// PHP sorting functions
|
|
314
|
+
/**
|
|
315
|
+
* Sort an array.
|
|
316
|
+
*
|
|
317
|
+
* @param {T[]} array The source array
|
|
318
|
+
* @returns {T[]} The sorted array (copy)
|
|
319
|
+
*/
|
|
320
|
+
export const sort = <T>(array: T[]): T[] => {
|
|
321
|
+
return [...array].sort()
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Sort an array in reverse order.
|
|
326
|
+
*
|
|
327
|
+
* @param {T[]} array The source array
|
|
328
|
+
* @returns {T[]} The sorted array (copy)
|
|
329
|
+
*/
|
|
330
|
+
export const rsort = <T>(array: T[]): T[] => {
|
|
331
|
+
return [...array].sort().reverse()
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Sort an array with a user-defined comparison function.
|
|
336
|
+
*
|
|
337
|
+
* @param {T[]} array The source array
|
|
338
|
+
* @param {Function} compareFunction The comparison function
|
|
339
|
+
* @returns {T[]} The sorted array (copy)
|
|
340
|
+
*/
|
|
341
|
+
export const usort = <T>(array: T[], compareFunction: (a: T, b: T) => number): T[] => {
|
|
342
|
+
return [...array].sort(compareFunction)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Sort an array with a user-defined comparison function and maintain index association.
|
|
347
|
+
*
|
|
348
|
+
* @param {T[]} array The source array
|
|
349
|
+
* @param {Function} compareFunction The comparison function
|
|
350
|
+
* @returns {T[]} The sorted array (copy)
|
|
351
|
+
*/
|
|
352
|
+
export const uasort = <T>(array: T[], compareFunction: (a: T, b: T) => number): T[] => {
|
|
353
|
+
return [...array].sort(compareFunction)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Sort an array by keys using a user-defined comparison function.
|
|
358
|
+
*
|
|
359
|
+
* @param {T[]} array The source array
|
|
360
|
+
* @param {Function} compareFunction The comparison function
|
|
361
|
+
* @returns {T[]} The sorted array (copy)
|
|
362
|
+
*/
|
|
363
|
+
export const uksort = <T>(array: T[], compareFunction: (a: number, b: number) => number): T[] => {
|
|
364
|
+
const indices = Array.from({ length: array.length }, (_, i) => i)
|
|
365
|
+
const sortedIndices = indices.sort(compareFunction)
|
|
366
|
+
return sortedIndices.map((i) => array[i])
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Shuffle an array.
|
|
371
|
+
*
|
|
372
|
+
* @param {T[]} array The source array
|
|
373
|
+
* @returns {T[]} The shuffled array (copy)
|
|
374
|
+
*/
|
|
375
|
+
export const shuffle = <T>(array: T[]): T[] => {
|
|
376
|
+
const result = [...array]
|
|
377
|
+
for (let i = result.length - 1; 0 < i; i--) {
|
|
378
|
+
const j = Math.floor(Math.random() * (i + 1))
|
|
379
|
+
;[result[i], result[j]] = [result[j], result[i]]
|
|
380
|
+
}
|
|
381
|
+
return result
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Range function like PHP
|
|
385
|
+
/**
|
|
386
|
+
* Create an array containing a range of elements.
|
|
387
|
+
*
|
|
388
|
+
* @param {number} start First value of the sequence
|
|
389
|
+
* @param {number} end The sequence is stopped when end is reached
|
|
390
|
+
* @param {number} [step=1] The increment used in the range
|
|
391
|
+
* @returns {number[]} The array of elements
|
|
392
|
+
*/
|
|
393
|
+
export const range = (start: number, end: number, step: number = 1): number[] => {
|
|
394
|
+
const result: number[] = []
|
|
395
|
+
|
|
396
|
+
if (0 < step) {
|
|
397
|
+
for (let i = start; i <= end; i += step) {
|
|
398
|
+
result.push(i)
|
|
399
|
+
}
|
|
400
|
+
} else if (0 > step) {
|
|
401
|
+
for (let i = start; i >= end; i += step) {
|
|
402
|
+
result.push(i)
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return result
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Create PHP-like array from JavaScript array
|
|
410
|
+
/**
|
|
411
|
+
* Create an EnhancedArray with PHP-like methods.
|
|
412
|
+
*
|
|
413
|
+
* @param {T[]} items The source items
|
|
414
|
+
* @returns {EnhancedArray<T>} The enhanced array
|
|
415
|
+
*/
|
|
416
|
+
export const php_array = <T>(items: T[]): EnhancedArray<T> => {
|
|
417
|
+
const enhanced = new EnhancedArray(...items)
|
|
418
|
+
|
|
419
|
+
// Add PHP-like methods
|
|
420
|
+
Object.assign(enhanced, {
|
|
421
|
+
// PHP array functions as methods
|
|
422
|
+
chunk: (size: number) => array_chunk(enhanced, size),
|
|
423
|
+
merge: (...arrays: T[][]) => new EnhancedArray(...array_merge(enhanced, ...arrays)),
|
|
424
|
+
unique: () => new EnhancedArray(...array_unique(enhanced)),
|
|
425
|
+
reverse: (preserveKeys = false) => new EnhancedArray(...array_reverse(enhanced, preserveKeys)),
|
|
426
|
+
search: (needle: T) => array_search(needle, enhanced),
|
|
427
|
+
sum: () => array_sum(enhanced as unknown as number[]),
|
|
428
|
+
product: () => array_product(enhanced as unknown as number[]),
|
|
429
|
+
rand: (num = 1) => array_rand(enhanced, num),
|
|
430
|
+
flip: () => array_flip(enhanced as unknown as (string | number)[]),
|
|
431
|
+
countValues: () => array_count_values(enhanced as unknown as (string | number)[]),
|
|
432
|
+
intersect: (...arrays: T[][]) => new EnhancedArray(...array_intersect(enhanced, ...arrays)),
|
|
433
|
+
diff: (...arrays: T[][]) => new EnhancedArray(...array_diff(enhanced, ...arrays)),
|
|
434
|
+
column: <K extends keyof T>(column: K) =>
|
|
435
|
+
(array_column as (arr: unknown[], col: unknown) => T[K][])(enhanced, column),
|
|
436
|
+
|
|
437
|
+
// PHP sorting as methods
|
|
438
|
+
sort: () => new EnhancedArray(...sort(enhanced)),
|
|
439
|
+
rsort: () => new EnhancedArray(...rsort(enhanced)),
|
|
440
|
+
shuffle: () => new EnhancedArray(...shuffle(enhanced)),
|
|
441
|
+
usort: (compareFunction: (a: T, b: T) => number) => new EnhancedArray(...usort(enhanced, compareFunction)),
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
return enhanced
|
|
445
|
+
}
|
package/src/pipe.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional pipe helper inspired by Laravel pipelines.
|
|
3
|
+
*
|
|
4
|
+
* @param {T} value The initial value to pipe
|
|
5
|
+
* @returns {Object} An object with through and value methods
|
|
6
|
+
*/
|
|
7
|
+
export function pipe<T>(value: T) {
|
|
8
|
+
return {
|
|
9
|
+
/**
|
|
10
|
+
* Pass the value through a callback.
|
|
11
|
+
*
|
|
12
|
+
* @param {Function} fn The callback function
|
|
13
|
+
* @returns {ReturnType<typeof pipe>} A new pipe instance with the result
|
|
14
|
+
*/
|
|
15
|
+
through<U>(fn: (input: T) => U) {
|
|
16
|
+
const result = fn(value)
|
|
17
|
+
return pipe(result)
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the current value of the pipe.
|
|
22
|
+
*
|
|
23
|
+
* @returns {T} The current value
|
|
24
|
+
*/
|
|
25
|
+
value() {
|
|
26
|
+
return value
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { GenericRecord } from './types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Runtime utility functions inspired by Laravel
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check if a value is a plain object (Record)
|
|
9
|
+
*
|
|
10
|
+
* @param {unknown} value The value to check
|
|
11
|
+
* @returns {value is GenericRecord} True if the value is a plain object
|
|
12
|
+
*/
|
|
13
|
+
export function isRecord(value: unknown): value is GenericRecord {
|
|
14
|
+
return null !== value && 'object' === typeof value && !Array.isArray(value)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Execute a callback and return the given value
|
|
19
|
+
* Useful for side effects in a chain
|
|
20
|
+
*
|
|
21
|
+
* @param {T} value The value to pass to the callback
|
|
22
|
+
* @param {(value: T) => void} callback The callback to execute
|
|
23
|
+
* @returns {T} The original value
|
|
24
|
+
*/
|
|
25
|
+
export const tap = <T>(value: T, callback: (value: T) => void): T => {
|
|
26
|
+
callback(value)
|
|
27
|
+
return value
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Conditionally execute a callback
|
|
32
|
+
*
|
|
33
|
+
* @param {unknown} condition The condition to check (value or zero-arg function returning boolean)
|
|
34
|
+
* @param {() => R} onTrue The callback to execute if the condition is truthy
|
|
35
|
+
* @param {() => R} [onFalse] The callback to execute if the condition is falsy
|
|
36
|
+
* @returns {R | undefined} The result of the executed callback or undefined
|
|
37
|
+
*/
|
|
38
|
+
export const when = <R>(condition: unknown, onTrue: () => R, onFalse?: () => R): R | undefined => {
|
|
39
|
+
const shouldExecute = 'function' === typeof condition ? (condition as () => boolean)() : Boolean(condition)
|
|
40
|
+
|
|
41
|
+
if (shouldExecute) {
|
|
42
|
+
return onTrue()
|
|
43
|
+
} else if (onFalse) {
|
|
44
|
+
return onFalse()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return undefined
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Return the first argument if it's truthy, otherwise return the second
|
|
52
|
+
*
|
|
53
|
+
* @param {T | (() => T)} val The value or a function that returns the value
|
|
54
|
+
* @returns {T} The resolved value
|
|
55
|
+
*/
|
|
56
|
+
export const value = <T>(val: T | (() => T)): T => {
|
|
57
|
+
return 'function' === typeof val ? (val as () => T)() : val
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get an item from an array or object using dot notation
|
|
62
|
+
*
|
|
63
|
+
* @param {unknown} target The object or array to search
|
|
64
|
+
* @param {string | string[]} key The dot-notated key or an array of keys
|
|
65
|
+
* @param {unknown} [defaultValue=null] The default value to return if the key is not found
|
|
66
|
+
* @returns {unknown} The found value or the default value
|
|
67
|
+
*/
|
|
68
|
+
export const data_get = (target: unknown, key: string | string[], defaultValue: unknown = null): unknown => {
|
|
69
|
+
if (!isRecord(target) && !Array.isArray(target)) {
|
|
70
|
+
return defaultValue
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const parts = Array.isArray(key) ? key : key.split('.')
|
|
74
|
+
let result: unknown = target
|
|
75
|
+
|
|
76
|
+
for (const part of parts) {
|
|
77
|
+
if (!isRecord(result) && !Array.isArray(result)) {
|
|
78
|
+
return defaultValue
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (part in (result as GenericRecord)) {
|
|
82
|
+
result = (result as GenericRecord)[part]
|
|
83
|
+
} else {
|
|
84
|
+
return defaultValue
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return result
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Set an item in an array or object using dot notation
|
|
93
|
+
*
|
|
94
|
+
* @param {T} target The object or array to modify
|
|
95
|
+
* @param {string | string[]} key The dot-notated key or an array of keys
|
|
96
|
+
* @param {unknown} value The value to set
|
|
97
|
+
* @returns {T} The modified target
|
|
98
|
+
*/
|
|
99
|
+
export const data_set = <T>(target: T, key: string | string[], value: unknown): T => {
|
|
100
|
+
if (!isRecord(target) && !Array.isArray(target)) {
|
|
101
|
+
return target
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const parts = Array.isArray(key) ? key : key.split('.')
|
|
105
|
+
let current: unknown = target
|
|
106
|
+
|
|
107
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
108
|
+
const part = parts[i]
|
|
109
|
+
|
|
110
|
+
if (!isRecord(current)) {
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const currentRecord = current as GenericRecord
|
|
115
|
+
|
|
116
|
+
if (!(part in currentRecord) || !isRecord(currentRecord[part])) {
|
|
117
|
+
currentRecord[part] = {} as GenericRecord
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
current = currentRecord[part]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (isRecord(current)) {
|
|
124
|
+
;(current as GenericRecord)[parts[parts.length - 1]] = value
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return target
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Returns the object if it exists, otherwise returns null (similar to optional() helper)
|
|
132
|
+
*
|
|
133
|
+
* @param {T} value The value to return if it exists
|
|
134
|
+
* @returns {T | null} The value or null
|
|
135
|
+
*/
|
|
136
|
+
export const optional = <T>(value: T): T | null => {
|
|
137
|
+
return value ?? null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Execute a callback with retry logic
|
|
142
|
+
*
|
|
143
|
+
* @param {number} times The number of times to retry
|
|
144
|
+
* @param {(attempt: number) => Promise<T> | T} callback The callback to execute
|
|
145
|
+
* @param {number} [sleepMilliseconds=0] The amount of time to wait between retries
|
|
146
|
+
* @returns {Promise<T>} The result of the callback
|
|
147
|
+
* @throws {Error} If all attempts fail
|
|
148
|
+
*/
|
|
149
|
+
export const retry = async <T>(
|
|
150
|
+
times: number,
|
|
151
|
+
callback: (attempt: number) => Promise<T> | T,
|
|
152
|
+
sleepMilliseconds = 0
|
|
153
|
+
): Promise<T> => {
|
|
154
|
+
let lastError: Error | undefined
|
|
155
|
+
|
|
156
|
+
for (let attempt = 1; attempt <= times; attempt++) {
|
|
157
|
+
try {
|
|
158
|
+
return await callback(attempt)
|
|
159
|
+
} catch (error) {
|
|
160
|
+
lastError = error instanceof Error ? error : new Error(String(error))
|
|
161
|
+
if (attempt < times && 0 < sleepMilliseconds) {
|
|
162
|
+
await new Promise((resolve) => setTimeout(resolve, sleepMilliseconds))
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
throw lastError ?? new Error(`Retry failed after ${times} attempts`)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Throw an error if a condition is met
|
|
172
|
+
*
|
|
173
|
+
* @param {unknown} condition The condition to check
|
|
174
|
+
* @param {Error | string} exception The error to throw or the error message
|
|
175
|
+
* @throws {Error} If the condition is met
|
|
176
|
+
*/
|
|
177
|
+
export const throw_if = (condition: unknown, exception: Error | string): void => {
|
|
178
|
+
if ('function' === typeof condition ? (condition as () => boolean)() : Boolean(condition)) {
|
|
179
|
+
throw 'string' === typeof exception ? new Error(exception) : exception
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Throw an error unless a condition is met
|
|
185
|
+
*
|
|
186
|
+
* @param {unknown} condition The condition to check
|
|
187
|
+
* @param {Error | string} exception The error to throw or the error message
|
|
188
|
+
* @throws {Error} If the condition is not met
|
|
189
|
+
*/
|
|
190
|
+
export const throw_unless = (condition: unknown, exception: Error | string): void => {
|
|
191
|
+
if (!('function' === typeof condition ? (condition as () => boolean)() : Boolean(condition))) {
|
|
192
|
+
throw 'string' === typeof exception ? new Error(exception) : exception
|
|
193
|
+
}
|
|
194
|
+
}
|