@mantiq/helpers 0.0.1

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/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @mantiq/helpers
2
+
3
+ Utility library for MantiqJS — Str, Arr, Num, Collection, HTTP client, and async utilities.
4
+
5
+ Part of [MantiqJS](https://github.com/abdullahkhan/mantiq) — a batteries-included TypeScript web framework for Bun.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ bun add @mantiq/helpers
11
+ ```
12
+
13
+ ## Documentation
14
+
15
+ See the [MantiqJS repository](https://github.com/abdullahkhan/mantiq) for full documentation.
16
+
17
+ ## License
18
+
19
+ MIT
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@mantiq/helpers",
3
+ "version": "0.0.1",
4
+ "description": "Str, Arr, Num, Collection utilities",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Abdullah Khan",
8
+ "homepage": "https://github.com/abdullahkhan/mantiq/tree/main/packages/helpers",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/abdullahkhan/mantiq.git",
12
+ "directory": "packages/helpers"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/abdullahkhan/mantiq/issues"
16
+ },
17
+ "keywords": [
18
+ "mantiq",
19
+ "mantiqjs",
20
+ "bun",
21
+ "typescript",
22
+ "framework",
23
+ "helpers"
24
+ ],
25
+ "engines": {
26
+ "bun": ">=1.1.0"
27
+ },
28
+ "main": "./src/index.ts",
29
+ "types": "./src/index.ts",
30
+ "exports": {
31
+ ".": {
32
+ "bun": "./src/index.ts",
33
+ "default": "./src/index.ts"
34
+ }
35
+ },
36
+ "files": [
37
+ "src/",
38
+ "package.json",
39
+ "README.md",
40
+ "LICENSE"
41
+ ],
42
+ "scripts": {
43
+ "build": "bun build ./src/index.ts --outdir ./dist --target bun",
44
+ "test": "bun test",
45
+ "typecheck": "tsc --noEmit",
46
+ "clean": "rm -rf dist"
47
+ },
48
+ "devDependencies": {
49
+ "bun-types": "latest",
50
+ "typescript": "^5.7.0"
51
+ }
52
+ }
package/src/Arr.ts ADDED
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Array utility functions with deep dot-notation support.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * Arr.get({ user: { name: 'John' } }, 'user.name') // 'John'
7
+ * Arr.pluck([{ id: 1 }, { id: 2 }], 'id') // [1, 2]
8
+ * Arr.groupBy([1, 2, 3, 4], (n) => n % 2 === 0 ? 'even' : 'odd')
9
+ * ```
10
+ */
11
+ export const Arr = {
12
+ /** Wrap a value in an array if it isn't one already */
13
+ wrap<T>(value: T | T[] | null | undefined): T[] {
14
+ if (value === null || value === undefined) return []
15
+ return Array.isArray(value) ? value : [value]
16
+ },
17
+
18
+ /** Flatten a nested array to a given depth (default: Infinity) */
19
+ flatten<T>(array: any[], depth = Infinity): T[] {
20
+ return array.flat(depth) as T[]
21
+ },
22
+
23
+ /** Split an array into chunks of a given size */
24
+ chunk<T>(array: T[], size: number): T[][] {
25
+ const result: T[][] = []
26
+ for (let i = 0; i < array.length; i += size) {
27
+ result.push(array.slice(i, i + size))
28
+ }
29
+ return result
30
+ },
31
+
32
+ /** Get a value from a nested object using dot notation */
33
+ get<T = any>(obj: any, path: string, defaultValue?: T): T {
34
+ const keys = path.split('.')
35
+ let result = obj
36
+ for (const key of keys) {
37
+ if (result === null || result === undefined) return defaultValue as T
38
+ result = result[key]
39
+ }
40
+ return (result === undefined ? defaultValue : result) as T
41
+ },
42
+
43
+ /** Set a value on a nested object using dot notation (mutates) */
44
+ set(obj: any, path: string, value: any): void {
45
+ const keys = path.split('.')
46
+ let current = obj
47
+ for (let i = 0; i < keys.length - 1; i++) {
48
+ const key = keys[i]!
49
+ if (current[key] === undefined || current[key] === null || typeof current[key] !== 'object') {
50
+ current[key] = {}
51
+ }
52
+ current = current[key]
53
+ }
54
+ current[keys[keys.length - 1]!] = value
55
+ },
56
+
57
+ /** Check if a key exists at a dot-notated path */
58
+ has(obj: any, path: string): boolean {
59
+ const keys = path.split('.')
60
+ let current = obj
61
+ for (const key of keys) {
62
+ if (current === null || current === undefined || typeof current !== 'object') return false
63
+ if (!(key in current)) return false
64
+ current = current[key]
65
+ }
66
+ return true
67
+ },
68
+
69
+ /** Remove a key at a dot-notated path (mutates) */
70
+ forget(obj: any, path: string): void {
71
+ const keys = path.split('.')
72
+ let current = obj
73
+ for (let i = 0; i < keys.length - 1; i++) {
74
+ const key = keys[i]!
75
+ if (current[key] === undefined || typeof current[key] !== 'object') return
76
+ current = current[key]
77
+ }
78
+ delete current[keys[keys.length - 1]!]
79
+ },
80
+
81
+ /** Flatten a nested object into dot-notation keys */
82
+ dot(obj: Record<string, any>, prefix = ''): Record<string, any> {
83
+ const result: Record<string, any> = {}
84
+ for (const [key, value] of Object.entries(obj)) {
85
+ const fullKey = prefix ? `${prefix}.${key}` : key
86
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
87
+ Object.assign(result, Arr.dot(value, fullKey))
88
+ } else {
89
+ result[fullKey] = value
90
+ }
91
+ }
92
+ return result
93
+ },
94
+
95
+ /** Expand a dot-notation object into a nested object */
96
+ undot(obj: Record<string, any>): Record<string, any> {
97
+ const result: Record<string, any> = {}
98
+ for (const [path, value] of Object.entries(obj)) {
99
+ Arr.set(result, path, value)
100
+ }
101
+ return result
102
+ },
103
+
104
+ /** Pluck a single property from each item. With 3 args, creates a keyed object. */
105
+ pluck<T>(array: Record<string, any>[], valueKey: string, keyKey?: string): T[] | Record<string, T> {
106
+ if (keyKey !== undefined) {
107
+ const result: Record<string, T> = {}
108
+ for (const item of array) {
109
+ result[Arr.get(item, keyKey)] = Arr.get(item, valueKey)
110
+ }
111
+ return result
112
+ }
113
+ return array.map((item) => Arr.get<T>(item, valueKey))
114
+ },
115
+
116
+ /** Key an array by a given property */
117
+ keyBy<T extends Record<string, any>>(array: T[], key: string | ((item: T) => string)): Record<string, T> {
118
+ const result: Record<string, T> = {}
119
+ for (const item of array) {
120
+ const k = typeof key === 'function' ? key(item) : Arr.get(item, key)
121
+ result[k] = item
122
+ }
123
+ return result
124
+ },
125
+
126
+ /** Group an array by a key or callback */
127
+ groupBy<T>(array: T[], key: string | ((item: T) => string)): Record<string, T[]> {
128
+ const result: Record<string, T[]> = {}
129
+ for (const item of array) {
130
+ const k = typeof key === 'function' ? key(item) : Arr.get(item as any, key)
131
+ if (!result[k]) result[k] = []
132
+ result[k].push(item)
133
+ }
134
+ return result
135
+ },
136
+
137
+ /** Sort by a key or callback */
138
+ sortBy<T>(array: T[], key: string | ((item: T) => any)): T[] {
139
+ return [...array].sort((a, b) => {
140
+ const va = typeof key === 'function' ? key(a) : Arr.get(a as any, key)
141
+ const vb = typeof key === 'function' ? key(b) : Arr.get(b as any, key)
142
+ if (va < vb) return -1
143
+ if (va > vb) return 1
144
+ return 0
145
+ })
146
+ },
147
+
148
+ /** Sort by a key in descending order */
149
+ sortByDesc<T>(array: T[], key: string | ((item: T) => any)): T[] {
150
+ return Arr.sortBy(array, key).reverse()
151
+ },
152
+
153
+ /** Get unique values */
154
+ unique<T>(array: T[], key?: string | ((item: T) => any)): T[] {
155
+ if (!key) return [...new Set(array)]
156
+ const seen = new Set()
157
+ return array.filter((item) => {
158
+ const k = typeof key === 'function' ? key(item) : Arr.get(item as any, key)
159
+ if (seen.has(k)) return false
160
+ seen.add(k)
161
+ return true
162
+ })
163
+ },
164
+
165
+ /** Shuffle an array (Fisher-Yates) */
166
+ shuffle<T>(array: T[]): T[] {
167
+ const result = [...array]
168
+ for (let i = result.length - 1; i > 0; i--) {
169
+ const j = Math.floor(Math.random() * (i + 1))
170
+ ;[result[i], result[j]] = [result[j]!, result[i]!]
171
+ }
172
+ return result
173
+ },
174
+
175
+ /** Get the first element matching a predicate (or the first element) */
176
+ first<T>(array: T[], predicate?: (item: T) => boolean): T | undefined {
177
+ if (!predicate) return array[0]
178
+ return array.find(predicate)
179
+ },
180
+
181
+ /** Get the last element matching a predicate (or the last element) */
182
+ last<T>(array: T[], predicate?: (item: T) => boolean): T | undefined {
183
+ if (!predicate) return array[array.length - 1]
184
+ for (let i = array.length - 1; i >= 0; i--) {
185
+ if (predicate(array[i]!)) return array[i]
186
+ }
187
+ return undefined
188
+ },
189
+
190
+ /** Get a random element from the array */
191
+ random<T>(array: T[]): T | undefined {
192
+ if (array.length === 0) return undefined
193
+ return array[Math.floor(Math.random() * array.length)]
194
+ },
195
+
196
+ /** Pick only the given keys from each object */
197
+ only<T extends Record<string, any>>(obj: T, keys: string[]): Partial<T> {
198
+ const result: any = {}
199
+ for (const key of keys) {
200
+ if (key in obj) result[key] = obj[key]
201
+ }
202
+ return result
203
+ },
204
+
205
+ /** Get all keys except the given ones */
206
+ except<T extends Record<string, any>>(obj: T, keys: string[]): Partial<T> {
207
+ const excluded = new Set(keys)
208
+ const result: any = {}
209
+ for (const key of Object.keys(obj)) {
210
+ if (!excluded.has(key)) result[key] = obj[key]
211
+ }
212
+ return result
213
+ },
214
+
215
+ /** Partition an array into two based on a predicate */
216
+ partition<T>(array: T[], predicate: (item: T) => boolean): [T[], T[]] {
217
+ const pass: T[] = []
218
+ const fail: T[] = []
219
+ for (const item of array) {
220
+ if (predicate(item)) pass.push(item)
221
+ else fail.push(item)
222
+ }
223
+ return [pass, fail]
224
+ },
225
+
226
+ /** Create a cross-join of multiple arrays */
227
+ crossJoin<T>(...arrays: T[][]): T[][] {
228
+ return arrays.reduce<T[][]>(
229
+ (acc, arr) => acc.flatMap((combo) => arr.map((item) => [...combo, item])),
230
+ [[]],
231
+ )
232
+ },
233
+
234
+ /** Count occurrences by a callback */
235
+ countBy<T>(array: T[], key: string | ((item: T) => string)): Record<string, number> {
236
+ const result: Record<string, number> = {}
237
+ for (const item of array) {
238
+ const k = typeof key === 'function' ? key(item) : Arr.get(item as any, key)
239
+ result[k] = (result[k] ?? 0) + 1
240
+ }
241
+ return result
242
+ },
243
+
244
+ /** Zip multiple arrays together */
245
+ zip<T>(...arrays: T[][]): T[][] {
246
+ const maxLen = Math.max(...arrays.map((a) => a.length))
247
+ const result: T[][] = []
248
+ for (let i = 0; i < maxLen; i++) {
249
+ result.push(arrays.map((a) => a[i]!))
250
+ }
251
+ return result
252
+ },
253
+
254
+ /** Create a range of numbers */
255
+ range(start: number, end: number, step = 1): number[] {
256
+ const result: number[] = []
257
+ if (step > 0) {
258
+ for (let i = start; i <= end; i += step) result.push(i)
259
+ } else {
260
+ for (let i = start; i >= end; i += step) result.push(i)
261
+ }
262
+ return result
263
+ },
264
+ }