@sylphx/flow 2.18.0 → 2.18.2

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.
@@ -1,543 +0,0 @@
1
- /**
2
- * Functional Programming Utilities
3
- * Core utilities for functional composition and transformation
4
- *
5
- * Principles:
6
- * - Pure functions (no side effects)
7
- * - Immutable data
8
- * - Function composition
9
- * - Point-free style support
10
- */
11
-
12
- // ============================================================================
13
- // FUNCTION COMPOSITION
14
- // ============================================================================
15
-
16
- /**
17
- * Pipe - Left-to-right function composition
18
- * Applies functions sequentially from left to right
19
- *
20
- * @example
21
- * const result = pipe(
22
- * 5,
23
- * x => x * 2, // 10
24
- * x => x + 3, // 13
25
- * x => x.toString() // "13"
26
- * );
27
- */
28
- export const pipe = <T>(value: T, ...fns: Array<(arg: any) => any>): any =>
29
- fns.reduce((acc, fn) => fn(acc), value);
30
-
31
- /**
32
- * Compose - Right-to-left function composition
33
- * Applies functions sequentially from right to left
34
- *
35
- * @example
36
- * const addThenMultiply = compose(
37
- * (x: number) => x * 2, // Applied second
38
- * (x: number) => x + 3 // Applied first
39
- * );
40
- * addThenMultiply(5); // (5 + 3) * 2 = 16
41
- */
42
- export const compose =
43
- <T>(...fns: Array<(arg: any) => any>) =>
44
- (value: T): any =>
45
- fns.reduceRight((acc, fn) => fn(acc), value);
46
-
47
- /**
48
- * Flow - Alias for pipe (for function composition without initial value)
49
- *
50
- * @example
51
- * const transform = flow(
52
- * (x: number) => x * 2,
53
- * (x: number) => x + 3,
54
- * (x: number) => x.toString()
55
- * );
56
- * transform(5); // "13"
57
- */
58
- export const flow =
59
- <A, B>(...fns: Array<(arg: any) => any>) =>
60
- (value: A): B =>
61
- pipe(value, ...fns);
62
-
63
- // ============================================================================
64
- // CURRYING & PARTIAL APPLICATION
65
- // ============================================================================
66
-
67
- /**
68
- * Curry - Transform multi-argument function to curried form
69
- *
70
- * @example
71
- * const add = (a: number, b: number) => a + b;
72
- * const curriedAdd = curry(add);
73
- * curriedAdd(5)(3); // 8
74
- */
75
- export const curry =
76
- <A, B, C>(fn: (a: A, b: B) => C) =>
77
- (a: A) =>
78
- (b: B): C =>
79
- fn(a, b);
80
-
81
- /**
82
- * Curry3 - Curry function with 3 arguments
83
- */
84
- export const curry3 =
85
- <A, B, C, D>(fn: (a: A, b: B, c: C) => D) =>
86
- (a: A) =>
87
- (b: B) =>
88
- (c: C): D =>
89
- fn(a, b, c);
90
-
91
- /**
92
- * Partial - Partial application of function arguments
93
- *
94
- * @example
95
- * const add = (a: number, b: number, c: number) => a + b + c;
96
- * const add5 = partial(add, 5);
97
- * add5(3, 2); // 10
98
- */
99
- export const partial =
100
- <A extends any[], R>(fn: (...args: A) => R, ...partialArgs: Partial<A>) =>
101
- (...remainingArgs: any[]): R =>
102
- fn(...([...partialArgs, ...remainingArgs] as A));
103
-
104
- // ============================================================================
105
- // ARRAY TRANSFORMATIONS (Point-free style)
106
- // ============================================================================
107
-
108
- /**
109
- * Map - Transform array elements
110
- *
111
- * @example
112
- * pipe(
113
- * [1, 2, 3],
114
- * map((x: number) => x * 2)
115
- * ); // [2, 4, 6]
116
- */
117
- export const map =
118
- <A, B>(fn: (item: A, index: number) => B) =>
119
- (items: readonly A[]): B[] =>
120
- items.map(fn);
121
-
122
- /**
123
- * Filter - Keep elements matching predicate
124
- *
125
- * @example
126
- * pipe(
127
- * [1, 2, 3, 4],
128
- * filter((x: number) => x > 2)
129
- * ); // [3, 4]
130
- */
131
- export const filter =
132
- <A>(predicate: (item: A, index: number) => boolean) =>
133
- (items: readonly A[]): A[] =>
134
- items.filter(predicate);
135
-
136
- /**
137
- * Reduce - Accumulate array to single value
138
- *
139
- * @example
140
- * pipe(
141
- * [1, 2, 3, 4],
142
- * reduce((acc: number, x: number) => acc + x, 0)
143
- * ); // 10
144
- */
145
- export const reduce =
146
- <A, B>(fn: (acc: B, item: A, index: number) => B, initial: B) =>
147
- (items: readonly A[]): B =>
148
- items.reduce(fn, initial);
149
-
150
- /**
151
- * FlatMap - Map then flatten
152
- *
153
- * @example
154
- * pipe(
155
- * [1, 2, 3],
156
- * flatMap((x: number) => [x, x * 2])
157
- * ); // [1, 2, 2, 4, 3, 6]
158
- */
159
- export const flatMap =
160
- <A, B>(fn: (item: A, index: number) => B[]) =>
161
- (items: readonly A[]): B[] =>
162
- items.flatMap(fn);
163
-
164
- /**
165
- * Sort - Sort array (returns new array)
166
- *
167
- * @example
168
- * pipe(
169
- * [3, 1, 2],
170
- * sort((a: number, b: number) => a - b)
171
- * ); // [1, 2, 3]
172
- */
173
- export const sort =
174
- <A>(compareFn: (a: A, b: A) => number) =>
175
- (items: readonly A[]): A[] =>
176
- [...items].sort(compareFn);
177
-
178
- /**
179
- * SortBy - Sort by property
180
- *
181
- * @example
182
- * pipe(
183
- * [{ age: 30 }, { age: 20 }, { age: 25 }],
184
- * sortBy('age')
185
- * ); // [{ age: 20 }, { age: 25 }, { age: 30 }]
186
- */
187
- export const sortBy =
188
- <A>(key: keyof A) =>
189
- (items: readonly A[]): A[] =>
190
- [...items].sort((a, b) => {
191
- if (a[key] < b[key]) {
192
- return -1;
193
- }
194
- if (a[key] > b[key]) {
195
- return 1;
196
- }
197
- return 0;
198
- });
199
-
200
- /**
201
- * Reverse - Reverse array (returns new array)
202
- */
203
- export const reverse = <A>(items: readonly A[]): A[] => [...items].reverse();
204
-
205
- /**
206
- * Take - Take first n elements
207
- *
208
- * @example
209
- * pipe([1, 2, 3, 4, 5], take(3)); // [1, 2, 3]
210
- */
211
- export const take =
212
- (n: number) =>
213
- <A>(items: readonly A[]): A[] =>
214
- items.slice(0, n);
215
-
216
- /**
217
- * Drop - Drop first n elements
218
- *
219
- * @example
220
- * pipe([1, 2, 3, 4, 5], drop(2)); // [3, 4, 5]
221
- */
222
- export const drop =
223
- (n: number) =>
224
- <A>(items: readonly A[]): A[] =>
225
- items.slice(n);
226
-
227
- /**
228
- * Unique - Remove duplicates
229
- *
230
- * @example
231
- * pipe([1, 2, 2, 3, 3, 3], unique); // [1, 2, 3]
232
- */
233
- export const unique = <A>(items: readonly A[]): A[] => [...new Set(items)];
234
-
235
- /**
236
- * UniqueBy - Remove duplicates by key
237
- *
238
- * @example
239
- * pipe(
240
- * [{ id: 1 }, { id: 2 }, { id: 1 }],
241
- * uniqueBy('id')
242
- * ); // [{ id: 1 }, { id: 2 }]
243
- */
244
- export const uniqueBy =
245
- <A>(key: keyof A) =>
246
- (items: readonly A[]): A[] => {
247
- const seen = new Set();
248
- return items.filter((item) => {
249
- const value = item[key];
250
- if (seen.has(value)) {
251
- return false;
252
- }
253
- seen.add(value);
254
- return true;
255
- });
256
- };
257
-
258
- /**
259
- * Partition - Split array by predicate
260
- *
261
- * @example
262
- * pipe(
263
- * [1, 2, 3, 4, 5],
264
- * partition((x: number) => x > 3)
265
- * ); // [[4, 5], [1, 2, 3]]
266
- */
267
- export const partition =
268
- <A>(predicate: (item: A) => boolean) =>
269
- (items: readonly A[]): [A[], A[]] => {
270
- const pass: A[] = [];
271
- const fail: A[] = [];
272
- for (const item of items) {
273
- (predicate(item) ? pass : fail).push(item);
274
- }
275
- return [pass, fail];
276
- };
277
-
278
- /**
279
- * GroupBy - Group items by key
280
- *
281
- * @example
282
- * pipe(
283
- * [{ type: 'a', val: 1 }, { type: 'b', val: 2 }, { type: 'a', val: 3 }],
284
- * groupBy('type')
285
- * ); // { a: [...], b: [...] }
286
- */
287
- export const groupBy =
288
- <A>(key: keyof A) =>
289
- (items: readonly A[]): Record<string, A[]> =>
290
- items.reduce(
291
- (acc, item) => {
292
- const groupKey = String(item[key]);
293
- if (!acc[groupKey]) {
294
- acc[groupKey] = [];
295
- }
296
- acc[groupKey].push(item);
297
- return acc;
298
- },
299
- {} as Record<string, A[]>
300
- );
301
-
302
- // ============================================================================
303
- // ASYNC TRANSFORMATIONS
304
- // ============================================================================
305
-
306
- /**
307
- * MapAsync - Map with async function
308
- *
309
- * @example
310
- * await pipe(
311
- * [1, 2, 3],
312
- * mapAsync(async (x) => x * 2)
313
- * ); // [2, 4, 6]
314
- */
315
- export const mapAsync =
316
- <A, B>(fn: (item: A, index: number) => Promise<B>) =>
317
- (items: readonly A[]): Promise<B[]> =>
318
- Promise.all(items.map(fn));
319
-
320
- /**
321
- * FilterAsync - Filter with async predicate
322
- */
323
- export const filterAsync =
324
- <A>(predicate: (item: A, index: number) => Promise<boolean>) =>
325
- async (items: readonly A[]): Promise<A[]> => {
326
- const results = await Promise.all(items.map(predicate));
327
- return items.filter((_, i) => results[i]);
328
- };
329
-
330
- /**
331
- * ReduceAsync - Reduce with async function
332
- */
333
- export const reduceAsync =
334
- <A, B>(fn: (acc: B, item: A, index: number) => Promise<B>, initial: B) =>
335
- async (items: readonly A[]): Promise<B> => {
336
- let acc = initial;
337
- for (let i = 0; i < items.length; i++) {
338
- acc = await fn(acc, items[i], i);
339
- }
340
- return acc;
341
- };
342
-
343
- // ============================================================================
344
- // SIDE EFFECTS & DEBUGGING
345
- // ============================================================================
346
-
347
- /**
348
- * Tap - Execute side effect without changing value
349
- *
350
- * @example
351
- * pipe(
352
- * 5,
353
- * x => x * 2,
354
- * tap((x) => console.log('Debug:', x)), // Logs: Debug: 10
355
- * x => x + 3
356
- * ); // 13
357
- */
358
- export const tap =
359
- <T>(fn: (value: T) => void) =>
360
- (value: T): T => {
361
- fn(value);
362
- return value;
363
- };
364
-
365
- /**
366
- * TapAsync - Execute async side effect
367
- */
368
- export const tapAsync =
369
- <T>(fn: (value: T) => Promise<void>) =>
370
- async (value: T): Promise<T> => {
371
- await fn(value);
372
- return value;
373
- };
374
-
375
- /**
376
- * Trace - Log value with label
377
- *
378
- * @example
379
- * pipe(
380
- * 5,
381
- * x => x * 2,
382
- * trace('After multiply'), // Logs: "After multiply: 10"
383
- * x => x + 3
384
- * );
385
- */
386
- export const trace =
387
- (label: string) =>
388
- <T>(value: T): T => {
389
- console.log(`${label}:`, value);
390
- return value;
391
- };
392
-
393
- // ============================================================================
394
- // ERROR HANDLING
395
- // ============================================================================
396
-
397
- /**
398
- * Result type - Represents success or failure
399
- */
400
- export type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
401
-
402
- /**
403
- * TryCatch - Convert exception to Result type
404
- *
405
- * @example
406
- * const result = tryCatch(() => JSON.parse(input));
407
- * if (result.ok) {
408
- * console.log(result.value);
409
- * } else {
410
- * console.error(result.error);
411
- * }
412
- */
413
- export const tryCatch = <T, E = Error>(fn: () => T): Result<T, E> => {
414
- try {
415
- return { ok: true, value: fn() };
416
- } catch (error) {
417
- return { ok: false, error: error as E };
418
- }
419
- };
420
-
421
- /**
422
- * TryCatchAsync - Async version of tryCatch
423
- */
424
- export const tryCatchAsync = async <T, E = Error>(fn: () => Promise<T>): Promise<Result<T, E>> => {
425
- try {
426
- return { ok: true, value: await fn() };
427
- } catch (error) {
428
- return { ok: false, error: error as E };
429
- }
430
- };
431
-
432
- /**
433
- * UnwrapResult - Extract value from Result or throw error
434
- */
435
- export const unwrapResult = <T, E>(result: Result<T, E>): T => {
436
- if (result.ok) {
437
- return result.value;
438
- }
439
- throw result.error;
440
- };
441
-
442
- /**
443
- * MapResult - Transform Result value
444
- */
445
- export const mapResult =
446
- <T, U, E>(fn: (value: T) => U) =>
447
- (result: Result<T, E>): Result<U, E> => {
448
- if (result.ok) {
449
- return { ok: true, value: fn(result.value) };
450
- }
451
- return result;
452
- };
453
-
454
- // ============================================================================
455
- // PREDICATES & LOGIC
456
- // ============================================================================
457
-
458
- /**
459
- * Not - Negate predicate
460
- */
461
- export const not =
462
- <A>(predicate: (item: A) => boolean) =>
463
- (item: A): boolean =>
464
- !predicate(item);
465
-
466
- /**
467
- * And - Combine predicates with AND
468
- */
469
- export const and =
470
- <A>(...predicates: Array<(item: A) => boolean>) =>
471
- (item: A): boolean =>
472
- predicates.every((p) => p(item));
473
-
474
- /**
475
- * Or - Combine predicates with OR
476
- */
477
- export const or =
478
- <A>(...predicates: Array<(item: A) => boolean>) =>
479
- (item: A): boolean =>
480
- predicates.some((p) => p(item));
481
-
482
- // ============================================================================
483
- // UTILITIES
484
- // ============================================================================
485
-
486
- /**
487
- * Identity - Return value as-is
488
- */
489
- export const identity = <T>(value: T): T => value;
490
-
491
- /**
492
- * Constant - Always return same value
493
- */
494
- export const constant =
495
- <T>(value: T) =>
496
- (): T =>
497
- value;
498
-
499
- /**
500
- * Noop - Do nothing
501
- */
502
- export const noop = (): void => {};
503
-
504
- /**
505
- * Prop - Extract property value
506
- *
507
- * @example
508
- * pipe(
509
- * [{ name: 'Alice' }, { name: 'Bob' }],
510
- * map(prop('name'))
511
- * ); // ['Alice', 'Bob']
512
- */
513
- export const prop =
514
- <K extends string | number | symbol>(key: K) =>
515
- <T extends Record<K, any>>(obj: T): T[K] =>
516
- obj[key];
517
-
518
- /**
519
- * Pick - Pick properties from object
520
- */
521
- export const pick =
522
- <T, K extends keyof T>(keys: K[]) =>
523
- (obj: T): Pick<T, K> =>
524
- keys.reduce(
525
- (acc, key) => {
526
- acc[key] = obj[key];
527
- return acc;
528
- },
529
- {} as Pick<T, K>
530
- );
531
-
532
- /**
533
- * Omit - Omit properties from object
534
- */
535
- export const omit =
536
- <T, K extends keyof T>(keys: K[]) =>
537
- (obj: T): Omit<T, K> => {
538
- const result = { ...obj };
539
- for (const key of keys) {
540
- delete result[key];
541
- }
542
- return result;
543
- };