@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/schemas.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime type validation schemas using Zod
|
|
3
|
+
* Provides runtime type checking that persists after TypeScript compilation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Email validation schema
|
|
10
|
+
*/
|
|
11
|
+
export const EmailSchema = z.string().email('Invalid email format').min(1, 'Email is required')
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Phone validation schema (E.164 format)
|
|
15
|
+
*/
|
|
16
|
+
export const PhoneSchema = z.string().regex(/^\+?[1-9]\d{1,14}$/, 'Invalid phone number format')
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* URL validation schema
|
|
20
|
+
*/
|
|
21
|
+
export const UrlSchema = z.string().url('Invalid URL format')
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Date validation schema (ISO datetime, YYYY-MM-DD, or Date object)
|
|
25
|
+
*/
|
|
26
|
+
export const DateSchema = z.union([
|
|
27
|
+
z.string().datetime(),
|
|
28
|
+
z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Invalid date format (YYYY-MM-DD)'),
|
|
29
|
+
z.date(),
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* API Response schema factory
|
|
34
|
+
*
|
|
35
|
+
* @param {T} dataSchema The schema for the data property
|
|
36
|
+
* @returns {z.ZodObject} The API response schema
|
|
37
|
+
*/
|
|
38
|
+
export const ApiResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) =>
|
|
39
|
+
z.object({
|
|
40
|
+
success: z.boolean(),
|
|
41
|
+
data: dataSchema.optional(),
|
|
42
|
+
message: z.string().optional(),
|
|
43
|
+
errors: z.array(z.string()).optional(),
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Pagination schema
|
|
48
|
+
*/
|
|
49
|
+
export const PaginationSchema = z.object({
|
|
50
|
+
page: z.number().int().positive().default(1),
|
|
51
|
+
perPage: z.number().int().min(1).max(100).default(15),
|
|
52
|
+
total: z.number().int().nonnegative(),
|
|
53
|
+
lastPage: z.number().int().positive(),
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Paginated response schema factory
|
|
58
|
+
*
|
|
59
|
+
* @param {T} itemSchema The schema for individual items in the data array
|
|
60
|
+
* @returns {z.ZodObject} The paginated response schema
|
|
61
|
+
*/
|
|
62
|
+
export const PaginatedResponseSchema = <T extends z.ZodTypeAny>(itemSchema: T) =>
|
|
63
|
+
z.object({
|
|
64
|
+
data: z.array(itemSchema),
|
|
65
|
+
pagination: PaginationSchema,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Type inferred from EmailSchema
|
|
70
|
+
*/
|
|
71
|
+
export type Email = z.infer<typeof EmailSchema>
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Type inferred from PhoneSchema
|
|
75
|
+
*/
|
|
76
|
+
export type Phone = z.infer<typeof PhoneSchema>
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Type inferred from UrlSchema
|
|
80
|
+
*/
|
|
81
|
+
export type Url = z.infer<typeof UrlSchema>
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Type inferred from ApiResponseSchema
|
|
85
|
+
*/
|
|
86
|
+
export type ApiResponse<T> = z.infer<ReturnType<typeof ApiResponseSchema<z.ZodType<T>>>>
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Type inferred from PaginationSchema
|
|
90
|
+
*/
|
|
91
|
+
export type Pagination = z.infer<typeof PaginationSchema>
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Type inferred from PaginatedResponseSchema
|
|
95
|
+
*/
|
|
96
|
+
export type PaginatedResponse<T> = z.infer<ReturnType<typeof PaginatedResponseSchema<z.ZodType<T>>>>
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Validates data against a schema and throws detailed errors
|
|
100
|
+
*
|
|
101
|
+
* @param {z.ZodSchema<T>} schema The schema to validate against
|
|
102
|
+
* @param {unknown} data The data to validate
|
|
103
|
+
* @returns {T} The validated data
|
|
104
|
+
* @throws {Error} If validation fails
|
|
105
|
+
*/
|
|
106
|
+
export const validateSchema = <T>(schema: z.ZodSchema<T>, data: unknown): T => {
|
|
107
|
+
const result = schema.safeParse(data)
|
|
108
|
+
|
|
109
|
+
if (!result.success) {
|
|
110
|
+
const errors = result.error.issues.map((err: z.ZodIssue) => `${err.path.join('.')}: ${err.message}`)
|
|
111
|
+
throw new Error(`Validation failed: ${errors.join(', ')}`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return result.data
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Validates data against a schema and returns result with success flag
|
|
119
|
+
*
|
|
120
|
+
* @param {z.ZodSchema<T>} schema The schema to validate against
|
|
121
|
+
* @param {unknown} data The data to validate
|
|
122
|
+
* @returns {{ success: true; data: T } | { success: false; errors: string[] }} The validation result
|
|
123
|
+
*/
|
|
124
|
+
export const safeValidateSchema = <T>(
|
|
125
|
+
schema: z.ZodSchema<T>,
|
|
126
|
+
data: unknown
|
|
127
|
+
): { success: true; data: T } | { success: false; errors: string[] } => {
|
|
128
|
+
const result = schema.safeParse(data)
|
|
129
|
+
|
|
130
|
+
if (result.success) {
|
|
131
|
+
return { success: true, data: result.data }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const errors = result.error.issues.map((err: z.ZodIssue) => `${err.path.join('.')}: ${err.message}`)
|
|
135
|
+
return { success: false, errors }
|
|
136
|
+
}
|
package/src/str.ts
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import * as formatters from './formatters.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fluent string manipulation class inspired by Laravel's Str and Stringable
|
|
5
|
+
*/
|
|
6
|
+
export class Stringable {
|
|
7
|
+
protected value: string
|
|
8
|
+
|
|
9
|
+
constructor(value: unknown) {
|
|
10
|
+
this.value = String(value ?? '')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get the raw string value
|
|
15
|
+
*
|
|
16
|
+
* @returns {string} The raw string
|
|
17
|
+
*/
|
|
18
|
+
toString(): string {
|
|
19
|
+
return this.value
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Alias for toString()
|
|
24
|
+
*
|
|
25
|
+
* @returns {string} The raw string
|
|
26
|
+
*/
|
|
27
|
+
get(): string {
|
|
28
|
+
return this.value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Convert to title case
|
|
33
|
+
*
|
|
34
|
+
* @returns {this} The Stringable instance
|
|
35
|
+
*/
|
|
36
|
+
title(): this {
|
|
37
|
+
this.value = formatters.titleCase(this.value)
|
|
38
|
+
return this
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Convert to slug
|
|
43
|
+
*
|
|
44
|
+
* @returns {this} The Stringable instance
|
|
45
|
+
*/
|
|
46
|
+
slug(): this {
|
|
47
|
+
this.value = formatters.slugify(this.value)
|
|
48
|
+
return this
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Convert to snake_case
|
|
53
|
+
*
|
|
54
|
+
* @returns {this} The Stringable instance
|
|
55
|
+
*/
|
|
56
|
+
snake(): this {
|
|
57
|
+
this.value = formatters.snakeCase(this.value)
|
|
58
|
+
return this
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Convert to kebab-case
|
|
63
|
+
*
|
|
64
|
+
* @returns {this} The Stringable instance
|
|
65
|
+
*/
|
|
66
|
+
kebab(): this {
|
|
67
|
+
this.value = formatters.kebabCase(this.value)
|
|
68
|
+
return this
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Convert to camelCase
|
|
73
|
+
*
|
|
74
|
+
* @returns {this} The Stringable instance
|
|
75
|
+
*/
|
|
76
|
+
camel(): this {
|
|
77
|
+
this.value = formatters.camelCase(this.value)
|
|
78
|
+
return this
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Convert to StudlyCase
|
|
83
|
+
*
|
|
84
|
+
* @returns {this} The Stringable instance
|
|
85
|
+
*/
|
|
86
|
+
studly(): this {
|
|
87
|
+
this.value = formatters.studlyCase(this.value)
|
|
88
|
+
return this
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Limit the string length
|
|
93
|
+
*
|
|
94
|
+
* @param {number} size The maximum length
|
|
95
|
+
* @param {string} [end='…'] The string to append if limited
|
|
96
|
+
* @returns {this} The Stringable instance
|
|
97
|
+
*/
|
|
98
|
+
limit(size: number, end = '…'): this {
|
|
99
|
+
this.value = formatters.strLimit(this.value, size)
|
|
100
|
+
if (this.value.endsWith('…') && '…' !== end) {
|
|
101
|
+
this.value = this.value.slice(0, -1) + end
|
|
102
|
+
}
|
|
103
|
+
return this
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Append a value to the string
|
|
108
|
+
*
|
|
109
|
+
* @param {...unknown[]} values The values to append
|
|
110
|
+
* @returns {this} The Stringable instance
|
|
111
|
+
*/
|
|
112
|
+
append(...values: unknown[]): this {
|
|
113
|
+
this.value += values.join('')
|
|
114
|
+
return this
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Prepend a value to the string
|
|
119
|
+
*
|
|
120
|
+
* @param {...unknown[]} values The values to prepend
|
|
121
|
+
* @returns {this} The Stringable instance
|
|
122
|
+
*/
|
|
123
|
+
prepend(...values: unknown[]): this {
|
|
124
|
+
this.value = values.join('') + this.value
|
|
125
|
+
return this
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Cap the string with a value if it doesn't already end with it
|
|
130
|
+
*
|
|
131
|
+
* @param {string} cap The string to end with
|
|
132
|
+
* @returns {this} The Stringable instance
|
|
133
|
+
*/
|
|
134
|
+
finish(cap: string): this {
|
|
135
|
+
this.value = formatters.strFinish(this.value, cap)
|
|
136
|
+
return this
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Start the string with a value if it doesn't already start with it
|
|
141
|
+
*
|
|
142
|
+
* @param {string} prefix The string to start with
|
|
143
|
+
* @returns {this} The Stringable instance
|
|
144
|
+
*/
|
|
145
|
+
start(prefix: string): this {
|
|
146
|
+
this.value = formatters.strStart(this.value, prefix)
|
|
147
|
+
return this
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Replace the first occurrence of a value
|
|
152
|
+
*
|
|
153
|
+
* @param {string | RegExp} search The value to search for
|
|
154
|
+
* @param {string} replace The value to replace with
|
|
155
|
+
* @returns {this} The Stringable instance
|
|
156
|
+
*/
|
|
157
|
+
replace(search: string | RegExp, replace: string): this {
|
|
158
|
+
this.value = this.value.replace(search, replace)
|
|
159
|
+
return this
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Replace all occurrences of a value
|
|
164
|
+
*
|
|
165
|
+
* @param {string | RegExp} search The value to search for
|
|
166
|
+
* @param {string} replace The value to replace with
|
|
167
|
+
* @returns {this} The Stringable instance
|
|
168
|
+
*/
|
|
169
|
+
replaceAll(search: string | RegExp, replace: string): this {
|
|
170
|
+
if ('string' === typeof search) {
|
|
171
|
+
this.value = this.value.split(search).join(replace)
|
|
172
|
+
} else {
|
|
173
|
+
const flags = search.flags.includes('g') ? search.flags : `${search.flags}g`
|
|
174
|
+
this.value = this.value.replace(new RegExp(search.source, flags), replace)
|
|
175
|
+
}
|
|
176
|
+
return this
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Reverse the string
|
|
181
|
+
*
|
|
182
|
+
* @returns {this} The Stringable instance
|
|
183
|
+
*/
|
|
184
|
+
reverse(): this {
|
|
185
|
+
this.value = formatters.strReverse(this.value)
|
|
186
|
+
return this
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Mask a portion of the string
|
|
191
|
+
*
|
|
192
|
+
* @param {string} character The masking character
|
|
193
|
+
* @param {number} index The starting index
|
|
194
|
+
* @param {number} [length] The number of characters to mask
|
|
195
|
+
* @returns {this} The Stringable instance
|
|
196
|
+
*/
|
|
197
|
+
mask(character: string, index: number, length?: number): this {
|
|
198
|
+
this.value = formatters.strMask(this.value, character, index, length)
|
|
199
|
+
return this
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Trim the string
|
|
204
|
+
*
|
|
205
|
+
* @param {string} [chars] The characters to trim (defaults to whitespace)
|
|
206
|
+
* @returns {this} The Stringable instance
|
|
207
|
+
*/
|
|
208
|
+
trim(chars?: string): this {
|
|
209
|
+
if (!chars) {
|
|
210
|
+
this.value = this.value.trim()
|
|
211
|
+
} else {
|
|
212
|
+
const pattern = new RegExp(`^[${chars}]+|[${chars}]+$`, 'g')
|
|
213
|
+
this.value = this.value.replace(pattern, '')
|
|
214
|
+
}
|
|
215
|
+
return this
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Convert to lower case
|
|
220
|
+
*
|
|
221
|
+
* @returns {this} The Stringable instance
|
|
222
|
+
*/
|
|
223
|
+
lower(): this {
|
|
224
|
+
this.value = this.value.toLowerCase()
|
|
225
|
+
return this
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Convert to upper case
|
|
230
|
+
*
|
|
231
|
+
* @returns {this} The Stringable instance
|
|
232
|
+
*/
|
|
233
|
+
upper(): this {
|
|
234
|
+
this.value = this.value.toUpperCase()
|
|
235
|
+
return this
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Capitalize the first letter
|
|
240
|
+
*
|
|
241
|
+
* @returns {this} The Stringable instance
|
|
242
|
+
*/
|
|
243
|
+
capitalize(): this {
|
|
244
|
+
this.value = formatters.capitalize(this.value)
|
|
245
|
+
return this
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Check if string contains a value
|
|
250
|
+
*
|
|
251
|
+
* @param {string | string[]} needles The values to search for
|
|
252
|
+
* @returns {boolean} True if the string contains any of the values
|
|
253
|
+
*/
|
|
254
|
+
contains(needles: string | string[]): boolean {
|
|
255
|
+
if (Array.isArray(needles)) {
|
|
256
|
+
return formatters.strContainsAny(this.value, needles)
|
|
257
|
+
}
|
|
258
|
+
return this.value.includes(needles)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Check if string contains all values
|
|
263
|
+
*
|
|
264
|
+
* @param {string[]} needles The values to search for
|
|
265
|
+
* @returns {boolean} True if the string contains all values
|
|
266
|
+
*/
|
|
267
|
+
containsAll(needles: string[]): boolean {
|
|
268
|
+
return formatters.strContainsAll(this.value, needles)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Check if string starts with a value
|
|
273
|
+
*
|
|
274
|
+
* @param {string | string[]} needles The values to check
|
|
275
|
+
* @returns {boolean} True if the string starts with any of the values
|
|
276
|
+
*/
|
|
277
|
+
startsWith(needles: string | string[]): boolean {
|
|
278
|
+
if (Array.isArray(needles)) {
|
|
279
|
+
return needles.some((needle) => this.value.startsWith(needle))
|
|
280
|
+
}
|
|
281
|
+
return this.value.startsWith(needles)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Check if string ends with a value
|
|
286
|
+
*
|
|
287
|
+
* @param {string | string[]} needles The values to check
|
|
288
|
+
* @returns {boolean} True if the string ends with any of the values
|
|
289
|
+
*/
|
|
290
|
+
endsWith(needles: string | string[]): boolean {
|
|
291
|
+
if (Array.isArray(needles)) {
|
|
292
|
+
return needles.some((needle) => this.value.endsWith(needle))
|
|
293
|
+
}
|
|
294
|
+
return this.value.endsWith(needles)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Execute a callback with the stringable and return the result
|
|
299
|
+
*
|
|
300
|
+
* @param {(str: this) => U} callback The callback to execute
|
|
301
|
+
* @returns {U} The result of the callback
|
|
302
|
+
*/
|
|
303
|
+
pipe<U>(callback: (str: this) => U): U {
|
|
304
|
+
return callback(this)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Conditionally execute a callback
|
|
309
|
+
*
|
|
310
|
+
* @param {boolean | (() => boolean)} condition The condition to check
|
|
311
|
+
* @param {(str: this) => void} callback The callback to execute
|
|
312
|
+
* @returns {this} The Stringable instance
|
|
313
|
+
*/
|
|
314
|
+
when(condition: boolean | (() => boolean), callback: (str: this) => void): this {
|
|
315
|
+
const shouldExecute = 'function' === typeof condition ? condition() : condition
|
|
316
|
+
if (shouldExecute) {
|
|
317
|
+
callback(this)
|
|
318
|
+
}
|
|
319
|
+
return this
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Execute a callback and return the stringable (for side effects)
|
|
324
|
+
*
|
|
325
|
+
* @param {(str: this) => void} callback The callback to execute
|
|
326
|
+
* @returns {this} The Stringable instance
|
|
327
|
+
*/
|
|
328
|
+
tap(callback: (str: this) => void): this {
|
|
329
|
+
callback(this)
|
|
330
|
+
return this
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Create a new fluent stringable instance
|
|
336
|
+
*
|
|
337
|
+
* @param {unknown} [value] The initial string value
|
|
338
|
+
* @returns {Stringable} A new Stringable instance
|
|
339
|
+
*/
|
|
340
|
+
export const str = (value?: unknown): Stringable => {
|
|
341
|
+
return new Stringable(value)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Static methods for string manipulation
|
|
346
|
+
*/
|
|
347
|
+
export const Str = {
|
|
348
|
+
/**
|
|
349
|
+
* Create a new fluent stringable instance
|
|
350
|
+
*
|
|
351
|
+
* @param {unknown} value The initial string value
|
|
352
|
+
* @returns {Stringable} A new Stringable instance
|
|
353
|
+
*/
|
|
354
|
+
of: (value: unknown) => new Stringable(value),
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Convert a string to a slug
|
|
358
|
+
*
|
|
359
|
+
* @param {string} value The string to slugify
|
|
360
|
+
* @returns {string} The slugified string
|
|
361
|
+
*/
|
|
362
|
+
slug: (value: string) => formatters.slugify(value),
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Convert a string to snake_case
|
|
366
|
+
*
|
|
367
|
+
* @param {string} value The string to convert
|
|
368
|
+
* @returns {string} The snake_case string
|
|
369
|
+
*/
|
|
370
|
+
snake: (value: string) => formatters.snakeCase(value),
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Convert a string to kebab-case
|
|
374
|
+
*
|
|
375
|
+
* @param {string} value The string to convert
|
|
376
|
+
* @returns {string} The kebab-case string
|
|
377
|
+
*/
|
|
378
|
+
kebab: (value: string) => formatters.kebabCase(value),
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Convert a string to camelCase
|
|
382
|
+
*
|
|
383
|
+
* @param {string} value The string to convert
|
|
384
|
+
* @returns {string} The camelCase string
|
|
385
|
+
*/
|
|
386
|
+
camel: (value: string) => formatters.camelCase(value),
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Convert a string to StudlyCase
|
|
390
|
+
*
|
|
391
|
+
* @param {string} value The string to convert
|
|
392
|
+
* @returns {string} The StudlyCase string
|
|
393
|
+
*/
|
|
394
|
+
studly: (value: string) => formatters.studlyCase(value),
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Convert a string to title case
|
|
398
|
+
*
|
|
399
|
+
* @param {string} value The string to convert
|
|
400
|
+
* @returns {string} The title case string
|
|
401
|
+
*/
|
|
402
|
+
title: (value: string) => formatters.titleCase(value),
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Limit the length of a string
|
|
406
|
+
*
|
|
407
|
+
* @param {string} value The string to limit
|
|
408
|
+
* @param {number} size The maximum length
|
|
409
|
+
* @param {string} [end='…'] The string to append if limited
|
|
410
|
+
* @returns {string} The limited string
|
|
411
|
+
*/
|
|
412
|
+
limit: (value: string, size: number, end = '…') => {
|
|
413
|
+
const result = formatters.strLimit(value, size)
|
|
414
|
+
return result.endsWith('…') && '…' !== end ? result.slice(0, -1) + end : result
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Generate a random alphanumeric string
|
|
419
|
+
*
|
|
420
|
+
* @param {number} [length=16] The length of the random string
|
|
421
|
+
* @returns {string} The random string
|
|
422
|
+
*/
|
|
423
|
+
random: (length = 16): string => {
|
|
424
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
425
|
+
let result = ''
|
|
426
|
+
for (let i = 0; i < length; i++) {
|
|
427
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length))
|
|
428
|
+
}
|
|
429
|
+
return result
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Generate a UUID (version 4)
|
|
434
|
+
*
|
|
435
|
+
* @returns {string} The generated UUID
|
|
436
|
+
*/
|
|
437
|
+
uuid: (): string => crypto.randomUUID(),
|
|
438
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common type definitions for the helpers package
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A generic record object with string keys and unknown values
|
|
7
|
+
*/
|
|
8
|
+
export type GenericRecord = Record<string, unknown>
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A type that can be an object record or an array
|
|
12
|
+
*/
|
|
13
|
+
export type DataContainer = GenericRecord | unknown[]
|