fp-pack 0.5.0 → 0.7.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 (82) hide show
  1. package/README.md +17 -15
  2. package/dist/fp-pack.umd.js +1 -1
  3. package/dist/fp-pack.umd.js.map +1 -1
  4. package/dist/implement/async/pipeAsync.d.ts +11 -0
  5. package/dist/implement/async/pipeAsync.d.ts.map +1 -1
  6. package/dist/implement/async/pipeAsync.mjs.map +1 -1
  7. package/dist/implement/async/pipeAsyncSideEffect.d.ts +11 -0
  8. package/dist/implement/async/pipeAsyncSideEffect.d.ts.map +1 -1
  9. package/dist/implement/async/pipeAsyncSideEffect.mjs.map +1 -1
  10. package/dist/implement/composition/compose.d.ts +25 -9
  11. package/dist/implement/composition/compose.d.ts.map +1 -1
  12. package/dist/implement/composition/compose.mjs.map +1 -1
  13. package/dist/implement/composition/compose.type-test.d.ts +35 -0
  14. package/dist/implement/composition/compose.type-test.d.ts.map +1 -0
  15. package/dist/implement/composition/compose.util.type-test.d.ts +31 -0
  16. package/dist/implement/composition/compose.util.type-test.d.ts.map +1 -0
  17. package/dist/implement/composition/curry.d.ts +5 -5
  18. package/dist/implement/composition/curry.d.ts.map +1 -1
  19. package/dist/implement/composition/curry.mjs.map +1 -1
  20. package/dist/implement/composition/index.d.ts +4 -0
  21. package/dist/implement/composition/index.d.ts.map +1 -1
  22. package/dist/implement/composition/pipe.d.ts +3 -0
  23. package/dist/implement/composition/pipe.d.ts.map +1 -1
  24. package/dist/implement/composition/pipe.mjs.map +1 -1
  25. package/dist/implement/composition/pipe.type-test.d.ts +117 -1
  26. package/dist/implement/composition/pipe.type-test.d.ts.map +1 -1
  27. package/dist/implement/composition/pipe.util.type-test.d.ts +75 -0
  28. package/dist/implement/composition/pipe.util.type-test.d.ts.map +1 -0
  29. package/dist/implement/composition/sideEffect.d.ts +1 -1
  30. package/dist/implement/composition/sideEffect.d.ts.map +1 -1
  31. package/dist/implement/composition/sideEffect.mjs.map +1 -1
  32. package/dist/implement/composition/tap0.d.ts +6 -0
  33. package/dist/implement/composition/tap0.d.ts.map +1 -0
  34. package/dist/implement/composition/tap0.mjs +9 -0
  35. package/dist/implement/composition/tap0.mjs.map +1 -0
  36. package/dist/implement/object/assocPath.d.ts +2 -1
  37. package/dist/implement/object/assocPath.d.ts.map +1 -1
  38. package/dist/implement/object/assocPath.mjs.map +1 -1
  39. package/dist/implement/object/dissocPath.d.ts +2 -1
  40. package/dist/implement/object/dissocPath.d.ts.map +1 -1
  41. package/dist/implement/object/dissocPath.mjs.map +1 -1
  42. package/dist/implement/object/index.d.ts +1 -0
  43. package/dist/implement/object/index.d.ts.map +1 -1
  44. package/dist/implement/object/path.d.ts +4 -2
  45. package/dist/implement/object/path.d.ts.map +1 -1
  46. package/dist/implement/object/path.mjs.map +1 -1
  47. package/dist/implement/object/pathKey.d.ts +2 -0
  48. package/dist/implement/object/pathKey.d.ts.map +1 -0
  49. package/dist/implement/object/pathOr.d.ts +5 -3
  50. package/dist/implement/object/pathOr.d.ts.map +1 -1
  51. package/dist/implement/object/pathOr.mjs.map +1 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.mjs +230 -228
  55. package/dist/index.mjs.map +1 -1
  56. package/dist/skills/fp-pack/SKILL.md +19 -16
  57. package/dist/skills/fp-pack.md +19 -16
  58. package/dist/stream/index.d.ts +1 -0
  59. package/dist/stream/index.d.ts.map +1 -1
  60. package/package.json +1 -1
  61. package/src/implement/async/pipeAsync.ts +71 -0
  62. package/src/implement/async/pipeAsyncSideEffect.ts +74 -0
  63. package/src/implement/composition/compose.test.ts +14 -0
  64. package/src/implement/composition/compose.ts +133 -21
  65. package/src/implement/composition/compose.type-test.ts +109 -0
  66. package/src/implement/composition/compose.util.type-test.ts +128 -0
  67. package/src/implement/composition/curry.ts +5 -5
  68. package/src/implement/composition/index.ts +4 -0
  69. package/src/implement/composition/pipe.ts +4 -0
  70. package/src/implement/composition/pipe.type-test.ts +269 -1
  71. package/src/implement/composition/pipe.util.type-test.ts +256 -0
  72. package/src/implement/composition/sideEffect.ts +1 -1
  73. package/src/implement/composition/tap0.test.ts +16 -0
  74. package/src/implement/composition/tap0.ts +10 -0
  75. package/src/implement/object/assocPath.ts +2 -2
  76. package/src/implement/object/dissocPath.ts +2 -2
  77. package/src/implement/object/index.ts +1 -0
  78. package/src/implement/object/path.ts +5 -3
  79. package/src/implement/object/pathKey.ts +1 -0
  80. package/src/implement/object/pathOr.ts +6 -4
  81. package/src/index.ts +3 -0
  82. package/src/stream/index.ts +1 -0
@@ -7,7 +7,7 @@ metadata:
7
7
 
8
8
  # fp-pack AI Agent Skills
9
9
 
10
- Document Version: 0.5.0
10
+ Document Version: 0.7.0
11
11
 
12
12
  This document provides guidelines for AI coding assistants when working in projects that use fp-pack. Follow these instructions to write clean, declarative, functional code using fp-pack's utilities.
13
13
 
@@ -129,7 +129,7 @@ const finalValue = runPipeResult(processDataPipeline(input));
129
129
  **Key SideEffect functions:**
130
130
  - `SideEffect.of(fn, label?)` - Create a side effect container
131
131
  - `isSideEffect(value)` - Type guard for **runtime checking** whether a value is a SideEffect
132
- - `runPipeResult<T, R>(result)` - Execute SideEffect or return value (call **OUTSIDE** pipelines, provide generics for type safety). **⚠️ CRITICAL:** `runPipeResult<T, R=any>` has default `R=any`, so using it without generics returns `any` type. Always provide generics for type safety
132
+ - `runPipeResult<T, R>(result)` - Execute SideEffect or return value (call **OUTSIDE** pipelines). If the input type is precise, inference is preserved. If the input is widened to `SideEffect<any>` or `any`, the result becomes `any` unless you provide generics.
133
133
  - `matchSideEffect(result, { value, effect })` - Pattern match on result
134
134
 
135
135
  **Type-safe result handling:**
@@ -147,32 +147,34 @@ const processNumbers = pipeSideEffect(
147
147
 
148
148
  const result = processNumbers([1, 2, 3, 4, 5]);
149
149
 
150
- // ✅ CORRECT: Use isSideEffect for runtime checking + provide generics to runPipeResult
150
+ // ✅ CORRECT: Use isSideEffect for runtime checking
151
151
  if (!isSideEffect(result)) {
152
152
  // TypeScript knows: result is number[]
153
153
  const sum: number = result.reduce((a, b) => a + b, 0);
154
154
  } else {
155
155
  // TypeScript knows: result is SideEffect<string>
156
- // But runPipeResult still returns number[] | string (not fully narrowed)
157
- const error = runPipeResult<number[], string>(result); // error: number[] | string
156
+ // runPipeResult returns number[] | string (not fully narrowed)
157
+ const error = runPipeResult(result); // number[] | string
158
158
  }
159
159
 
160
- // WRONG: runPipeResult without generics
161
- const value = runPipeResult(result); // result: any (no type information!)
160
+ // ⚠️ If the result type is widened to SideEffect<any>, inference is lost
161
+ const widened: number[] | SideEffect<any> = result;
162
+ const unsafeValue = runPipeResult(widened); // number[] | any
162
163
 
163
- // ✅ CORRECT: Provide generics to runPipeResult
164
- const value = runPipeResult<number[], string>(result); // result: number[] | string (union type - safe but not narrowed)
164
+ // ✅ CORRECT: Provide generics to recover a safe union
165
+ const safeValue = runPipeResult<number[], string>(result); // result: number[] | string (union type - safe but not narrowed)
165
166
  ```
166
167
 
167
168
  **⚠️ CRITICAL: runPipeResult Type Safety**
168
169
 
169
170
  `runPipeResult<T, R=any>` has a default type parameter `R=any`. This means:
170
171
 
171
- - **Without generics**: `const result = runPipeResult(pipeline(data));` returns `any` type (unsafe!)
172
- - **With generics**: `runPipeResult<SuccessType, ErrorType>(result)` returns union type `SuccessType | ErrorType` (type-safe)
173
- - ✅ **With isSideEffect**: Use for runtime checking whether a value is SideEffect
172
+ - **Precise input types**: `T | SideEffect<'E'>` preserves `T | 'E'` without extra annotations.
173
+ - ⚠️ **Widened inputs**: `T | SideEffect<any>` (or `any`) collapses to `any`.
174
+ - ✅ **With generics**: `runPipeResult<SuccessType, ErrorType>(result)` restores a safe union when inference is lost.
175
+ - ✅ **With isSideEffect**: Prefer for runtime narrowing when you need branch-specific types.
174
176
 
175
- **Always provide generics to `runPipeResult`** for type safety. Use `isSideEffect` for runtime type checking.
177
+ Provide generics when inference is lost; prefer `isSideEffect` for precise narrowing.
176
178
 
177
179
  ## Stream Functions - Lazy Iterable Processing
178
180
 
@@ -219,6 +221,7 @@ const result = Array.from({ length: 1000000 }, (_, i) => i + 1)
219
221
  - `constant` - Always return the same value
220
222
  - `from` - Ignore input and return a fixed value
221
223
  - `tap` - Execute side effect and return original value
224
+ - `tap0` - Execute side effect without input
222
225
  - `once` - Execute function only once
223
226
  - `memoize` - Cache function results
224
227
  - `SideEffect` - Side effect container
@@ -527,7 +530,7 @@ const gradeToLetter = cond([
527
530
 
528
531
  - `prop` returns `T[K] | undefined`. Use `propOr` (or guard) before array operations.
529
532
  - `ifElse` expects **functions** for both branches. If you already have a value, wrap it: `() => value` or use `from(value)` for cleaner constant branches.
530
- - Use `from(value)` when you need a unary function that ignores input (handy for `ifElse`/`cond` branches and data-first patterns).
533
+ - Use `from(value)` when you need a unary function that ignores input (handy for `ifElse`/`cond` branches and data-first patterns). Pipelines that start with `from(...)` can be called without an initial input value.
531
534
  - `cond` returns `R | undefined`. Add a default branch and coalesce when you need a strict result.
532
535
  - In `pipeSideEffect`, keep step return types aligned to avoid wide unions.
533
536
 
@@ -680,8 +683,8 @@ const updateUser = assoc('lastLogin', new Date());
680
683
  - **Pure async operations**: `pipeAsync`
681
684
  - **Error handling with SideEffect**: `pipeSideEffect` (sync) / `pipeAsyncSideEffect` (async)
682
685
  - **Strict SideEffect unions**: `pipeSideEffectStrict` (sync) / `pipeAsyncSideEffectStrict` (async)
683
- - **Type-safe result handling**: `isSideEffect` for precise type narrowing (⚠️ **ALWAYS prefer this over bare `runPipeResult`** to avoid `any` types)
684
- - **Execute SideEffect**: `runPipeResult` (call OUTSIDE pipelines). **⚠️ CRITICAL:** Returns `any` type without type narrowing due to default `R=any` parameter
686
+ - **Type-safe result handling**: `isSideEffect` for precise type narrowing (prefer this when you need branch-specific types)
687
+ - **Execute SideEffect**: `runPipeResult` (call OUTSIDE pipelines). If the input is widened to `SideEffect<any>`/`any`, the result becomes `any`; provide generics to recover
685
688
  - **Large datasets**: `stream/*` functions
686
689
  - **Conditionals**: `ifElse`, `when`, `unless`, `cond`
687
690
  - **Object access**: `prop`, `propStrict`, `path`, `pick`, `omit`
@@ -1,6 +1,6 @@
1
1
  # fp-pack AI Agent Skills
2
2
 
3
- Document Version: 0.5.0
3
+ Document Version: 0.7.0
4
4
 
5
5
  This document provides guidelines for AI coding assistants when working in projects that use fp-pack. Follow these instructions to write clean, declarative, functional code using fp-pack's utilities.
6
6
 
@@ -122,7 +122,7 @@ const finalValue = runPipeResult(processDataPipeline(input));
122
122
  **Key SideEffect functions:**
123
123
  - `SideEffect.of(fn, label?)` - Create a side effect container
124
124
  - `isSideEffect(value)` - Type guard for **runtime checking** whether a value is a SideEffect
125
- - `runPipeResult<T, R>(result)` - Execute SideEffect or return value (call **OUTSIDE** pipelines, provide generics for type safety). **⚠️ CRITICAL:** `runPipeResult<T, R=any>` has default `R=any`, so using it without generics returns `any` type. Always provide generics for type safety
125
+ - `runPipeResult<T, R>(result)` - Execute SideEffect or return value (call **OUTSIDE** pipelines). If the input type is precise, inference is preserved. If the input is widened to `SideEffect<any>` or `any`, the result becomes `any` unless you provide generics.
126
126
  - `matchSideEffect(result, { value, effect })` - Pattern match on result
127
127
 
128
128
  **Type-safe result handling:**
@@ -140,32 +140,34 @@ const processNumbers = pipeSideEffect(
140
140
 
141
141
  const result = processNumbers([1, 2, 3, 4, 5]);
142
142
 
143
- // ✅ CORRECT: Use isSideEffect for runtime checking + provide generics to runPipeResult
143
+ // ✅ CORRECT: Use isSideEffect for runtime checking
144
144
  if (!isSideEffect(result)) {
145
145
  // TypeScript knows: result is number[]
146
146
  const sum: number = result.reduce((a, b) => a + b, 0);
147
147
  } else {
148
148
  // TypeScript knows: result is SideEffect<string>
149
- // But runPipeResult still returns number[] | string (not fully narrowed)
150
- const error = runPipeResult<number[], string>(result); // error: number[] | string
149
+ // runPipeResult returns number[] | string (not fully narrowed)
150
+ const error = runPipeResult(result); // number[] | string
151
151
  }
152
152
 
153
- // WRONG: runPipeResult without generics
154
- const value = runPipeResult(result); // result: any (no type information!)
153
+ // ⚠️ If the result type is widened to SideEffect<any>, inference is lost
154
+ const widened: number[] | SideEffect<any> = result;
155
+ const unsafeValue = runPipeResult(widened); // number[] | any
155
156
 
156
- // ✅ CORRECT: Provide generics to runPipeResult
157
- const value = runPipeResult<number[], string>(result); // result: number[] | string (union type - safe but not narrowed)
157
+ // ✅ CORRECT: Provide generics to recover a safe union
158
+ const safeValue = runPipeResult<number[], string>(result); // result: number[] | string (union type - safe but not narrowed)
158
159
  ```
159
160
 
160
161
  **⚠️ CRITICAL: runPipeResult Type Safety**
161
162
 
162
163
  `runPipeResult<T, R=any>` has a default type parameter `R=any`. This means:
163
164
 
164
- - **Without generics**: `const result = runPipeResult(pipeline(data));` returns `any` type (unsafe!)
165
- - **With generics**: `runPipeResult<SuccessType, ErrorType>(result)` returns union type `SuccessType | ErrorType` (type-safe)
166
- - ✅ **With isSideEffect**: Use for runtime checking whether a value is SideEffect
165
+ - **Precise input types**: `T | SideEffect<'E'>` preserves `T | 'E'` without extra annotations.
166
+ - ⚠️ **Widened inputs**: `T | SideEffect<any>` (or `any`) collapses to `any`.
167
+ - ✅ **With generics**: `runPipeResult<SuccessType, ErrorType>(result)` restores a safe union when inference is lost.
168
+ - ✅ **With isSideEffect**: Prefer for runtime narrowing when you need branch-specific types.
167
169
 
168
- **Always provide generics to `runPipeResult`** for type safety. Use `isSideEffect` for runtime type checking.
170
+ Provide generics when inference is lost; prefer `isSideEffect` for precise narrowing.
169
171
 
170
172
  ## Stream Functions - Lazy Iterable Processing
171
173
 
@@ -212,6 +214,7 @@ const result = Array.from({ length: 1000000 }, (_, i) => i + 1)
212
214
  - `constant` - Always return the same value
213
215
  - `from` - Ignore input and return a fixed value
214
216
  - `tap` - Execute side effect and return original value
217
+ - `tap0` - Execute side effect without input
215
218
  - `once` - Execute function only once
216
219
  - `memoize` - Cache function results
217
220
  - `SideEffect` - Side effect container
@@ -520,7 +523,7 @@ const gradeToLetter = cond([
520
523
 
521
524
  - `prop` returns `T[K] | undefined`. Use `propOr` (or guard) before array operations.
522
525
  - `ifElse` expects **functions** for both branches. If you already have a value, wrap it: `() => value` or use `from(value)` for cleaner constant branches.
523
- - Use `from(value)` when you need a unary function that ignores input (handy for `ifElse`/`cond` branches and data-first patterns).
526
+ - Use `from(value)` when you need a unary function that ignores input (handy for `ifElse`/`cond` branches and data-first patterns). Pipelines that start with `from(...)` can be called without an initial input value.
524
527
  - `cond` returns `R | undefined`. Add a default branch and coalesce when you need a strict result.
525
528
  - In `pipeSideEffect`, keep step return types aligned to avoid wide unions.
526
529
 
@@ -673,8 +676,8 @@ const updateUser = assoc('lastLogin', new Date());
673
676
  - **Pure async operations**: `pipeAsync`
674
677
  - **Error handling with SideEffect**: `pipeSideEffect` (sync) / `pipeAsyncSideEffect` (async)
675
678
  - **Strict SideEffect unions**: `pipeSideEffectStrict` (sync) / `pipeAsyncSideEffectStrict` (async)
676
- - **Type-safe result handling**: `isSideEffect` for precise type narrowing (⚠️ **ALWAYS prefer this over bare `runPipeResult`** to avoid `any` types)
677
- - **Execute SideEffect**: `runPipeResult` (call OUTSIDE pipelines). **⚠️ CRITICAL:** Returns `any` type without type narrowing due to default `R=any` parameter
679
+ - **Type-safe result handling**: `isSideEffect` for precise type narrowing (prefer this when you need branch-specific types)
680
+ - **Execute SideEffect**: `runPipeResult` (call OUTSIDE pipelines). If the input is widened to `SideEffect<any>`/`any`, the result becomes `any`; provide generics to recover
678
681
  - **Large datasets**: `stream/*` functions
679
682
  - **Conditionals**: `ifElse`, `when`, `unless`, `cond`
680
683
  - **Object access**: `prop`, `propStrict`, `path`, `pick`, `omit`
@@ -21,4 +21,5 @@ export { default as toArray } from './toArray';
21
21
  export { default as toAsync } from './toAsync';
22
22
  export { default as zip } from './zip';
23
23
  export { default as zipWith } from './zipWith';
24
+ export type { AnyIterable, AnyIterableInput, PromiseLikeValue } from './utils';
24
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stream/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stream/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAC/C,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fp-pack",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "description": "Functional programming utilities library for TypeScript",
6
6
  "keywords": [
@@ -2,6 +2,7 @@ import type { FromFn } from '../composition/from';
2
2
 
3
3
  /** pipeAsync - 비동기 함수 합성 */
4
4
  type AsyncOrSync<A, R> = (a: A) => R | Promise<R>;
5
+ type ZeroFn<R> = () => R | Promise<R>;
5
6
  type PipeInput<Fns extends AsyncOrSync<any, any>[]> = Fns extends [AsyncOrSync<infer A, any>, ...AsyncOrSync<any, any>[]]
6
7
  ? A
7
8
  : never;
@@ -17,6 +18,76 @@ type PipeAsyncFrom<Fns extends [FromFn<any>, ...AsyncOrSync<any, any>[]]> = (
17
18
  input?: PipeInput<Fns>
18
19
  ) => Promise<PipeOutput<Fns>>;
19
20
 
21
+ function pipeAsync<R>(ab: ZeroFn<R>): () => Promise<Awaited<R>>;
22
+ function pipeAsync<B, R>(ab: ZeroFn<B>, bc: AsyncOrSync<Awaited<B>, R>): () => Promise<Awaited<R>>;
23
+ function pipeAsync<B, C, R>(
24
+ ab: ZeroFn<B>,
25
+ bc: AsyncOrSync<Awaited<B>, C>,
26
+ cd: AsyncOrSync<Awaited<C>, R>
27
+ ): () => Promise<Awaited<R>>;
28
+ function pipeAsync<B, C, D, R>(
29
+ ab: ZeroFn<B>,
30
+ bc: AsyncOrSync<Awaited<B>, C>,
31
+ cd: AsyncOrSync<Awaited<C>, D>,
32
+ de: AsyncOrSync<Awaited<D>, R>
33
+ ): () => Promise<Awaited<R>>;
34
+ function pipeAsync<B, C, D, E, R>(
35
+ ab: ZeroFn<B>,
36
+ bc: AsyncOrSync<Awaited<B>, C>,
37
+ cd: AsyncOrSync<Awaited<C>, D>,
38
+ de: AsyncOrSync<Awaited<D>, E>,
39
+ ef: AsyncOrSync<Awaited<E>, R>
40
+ ): () => Promise<Awaited<R>>;
41
+ function pipeAsync<B, C, D, E, F, R>(
42
+ ab: ZeroFn<B>,
43
+ bc: AsyncOrSync<Awaited<B>, C>,
44
+ cd: AsyncOrSync<Awaited<C>, D>,
45
+ de: AsyncOrSync<Awaited<D>, E>,
46
+ ef: AsyncOrSync<Awaited<E>, F>,
47
+ fg: AsyncOrSync<Awaited<F>, R>
48
+ ): () => Promise<Awaited<R>>;
49
+ function pipeAsync<B, C, D, E, F, G, R>(
50
+ ab: ZeroFn<B>,
51
+ bc: AsyncOrSync<Awaited<B>, C>,
52
+ cd: AsyncOrSync<Awaited<C>, D>,
53
+ de: AsyncOrSync<Awaited<D>, E>,
54
+ ef: AsyncOrSync<Awaited<E>, F>,
55
+ fg: AsyncOrSync<Awaited<F>, G>,
56
+ gh: AsyncOrSync<Awaited<G>, R>
57
+ ): () => Promise<Awaited<R>>;
58
+ function pipeAsync<B, C, D, E, F, G, H, R>(
59
+ ab: ZeroFn<B>,
60
+ bc: AsyncOrSync<Awaited<B>, C>,
61
+ cd: AsyncOrSync<Awaited<C>, D>,
62
+ de: AsyncOrSync<Awaited<D>, E>,
63
+ ef: AsyncOrSync<Awaited<E>, F>,
64
+ fg: AsyncOrSync<Awaited<F>, G>,
65
+ gh: AsyncOrSync<Awaited<G>, H>,
66
+ hi: AsyncOrSync<Awaited<H>, R>
67
+ ): () => Promise<Awaited<R>>;
68
+ function pipeAsync<B, C, D, E, F, G, H, I, R>(
69
+ ab: ZeroFn<B>,
70
+ bc: AsyncOrSync<Awaited<B>, C>,
71
+ cd: AsyncOrSync<Awaited<C>, D>,
72
+ de: AsyncOrSync<Awaited<D>, E>,
73
+ ef: AsyncOrSync<Awaited<E>, F>,
74
+ fg: AsyncOrSync<Awaited<F>, G>,
75
+ gh: AsyncOrSync<Awaited<G>, H>,
76
+ hi: AsyncOrSync<Awaited<H>, I>,
77
+ ij: AsyncOrSync<Awaited<I>, R>
78
+ ): () => Promise<Awaited<R>>;
79
+ function pipeAsync<B, C, D, E, F, G, H, I, J, R>(
80
+ ab: ZeroFn<B>,
81
+ bc: AsyncOrSync<Awaited<B>, C>,
82
+ cd: AsyncOrSync<Awaited<C>, D>,
83
+ de: AsyncOrSync<Awaited<D>, E>,
84
+ ef: AsyncOrSync<Awaited<E>, F>,
85
+ fg: AsyncOrSync<Awaited<F>, G>,
86
+ gh: AsyncOrSync<Awaited<G>, H>,
87
+ hi: AsyncOrSync<Awaited<H>, I>,
88
+ ij: AsyncOrSync<Awaited<I>, J>,
89
+ jk: AsyncOrSync<Awaited<J>, R>
90
+ ): () => Promise<Awaited<R>>;
20
91
  function pipeAsync<Fns extends [FromFn<any>, ...AsyncOrSync<any, any>[]]>(...funcs: Fns): PipeAsyncFrom<Fns>;
21
92
  function pipeAsync<A, R>(ab: AsyncOrSync<A, R>): (a: A) => Promise<Awaited<R>>;
22
93
  function pipeAsync<A, B, R>(ab: AsyncOrSync<A, B>, bc: AsyncOrSync<Awaited<B>, R>): (a: A) => Promise<Awaited<R>>;
@@ -5,6 +5,7 @@ import SideEffect, { isSideEffect } from '../composition/sideEffect';
5
5
  type MaybeSideEffect<T> = T | SideEffect<any>;
6
6
  type NonSideEffect<T> = Exclude<T, SideEffect<any>>;
7
7
  type AsyncOrSync<A, R> = (a: A) => MaybeSideEffect<R> | Promise<MaybeSideEffect<R>>;
8
+ type ZeroFn<R> = () => MaybeSideEffect<R> | Promise<MaybeSideEffect<R>>;
8
9
  type PipeInput<Fns extends AsyncOrSync<any, any>[]> = Fns extends [
9
10
  AsyncOrSync<infer A, any>,
10
11
  ...AsyncOrSync<any, any>[]
@@ -29,6 +30,79 @@ type PipeAsyncSideEffectFrom<Fns extends [FromFn<any>, ...AsyncOrSync<any, any>[
29
30
  function pipeAsyncSideEffect<Fns extends [FromFn<any>, ...AsyncOrSync<any, any>[]]>(
30
31
  ...funcs: Fns
31
32
  ): PipeAsyncSideEffectFrom<Fns>;
33
+ function pipeAsyncSideEffect<R>(ab: ZeroFn<R>): () => Promise<MaybeSideEffect<Awaited<R>>>;
34
+ function pipeAsyncSideEffect<B, R>(
35
+ ab: ZeroFn<B>,
36
+ bc: AsyncOrSync<Awaited<B>, R>
37
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
38
+ function pipeAsyncSideEffect<B, C, R>(
39
+ ab: ZeroFn<B>,
40
+ bc: AsyncOrSync<Awaited<B>, C>,
41
+ cd: AsyncOrSync<Awaited<C>, R>
42
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
43
+ function pipeAsyncSideEffect<B, C, D, R>(
44
+ ab: ZeroFn<B>,
45
+ bc: AsyncOrSync<Awaited<B>, C>,
46
+ cd: AsyncOrSync<Awaited<C>, D>,
47
+ de: AsyncOrSync<Awaited<D>, R>
48
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
49
+ function pipeAsyncSideEffect<B, C, D, E, R>(
50
+ ab: ZeroFn<B>,
51
+ bc: AsyncOrSync<Awaited<B>, C>,
52
+ cd: AsyncOrSync<Awaited<C>, D>,
53
+ de: AsyncOrSync<Awaited<D>, E>,
54
+ ef: AsyncOrSync<Awaited<E>, R>
55
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
56
+ function pipeAsyncSideEffect<B, C, D, E, F, R>(
57
+ ab: ZeroFn<B>,
58
+ bc: AsyncOrSync<Awaited<B>, C>,
59
+ cd: AsyncOrSync<Awaited<C>, D>,
60
+ de: AsyncOrSync<Awaited<D>, E>,
61
+ ef: AsyncOrSync<Awaited<E>, F>,
62
+ fg: AsyncOrSync<Awaited<F>, R>
63
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
64
+ function pipeAsyncSideEffect<B, C, D, E, F, G, R>(
65
+ ab: ZeroFn<B>,
66
+ bc: AsyncOrSync<Awaited<B>, C>,
67
+ cd: AsyncOrSync<Awaited<C>, D>,
68
+ de: AsyncOrSync<Awaited<D>, E>,
69
+ ef: AsyncOrSync<Awaited<E>, F>,
70
+ fg: AsyncOrSync<Awaited<F>, G>,
71
+ gh: AsyncOrSync<Awaited<G>, R>
72
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
73
+ function pipeAsyncSideEffect<B, C, D, E, F, G, H, R>(
74
+ ab: ZeroFn<B>,
75
+ bc: AsyncOrSync<Awaited<B>, C>,
76
+ cd: AsyncOrSync<Awaited<C>, D>,
77
+ de: AsyncOrSync<Awaited<D>, E>,
78
+ ef: AsyncOrSync<Awaited<E>, F>,
79
+ fg: AsyncOrSync<Awaited<F>, G>,
80
+ gh: AsyncOrSync<Awaited<G>, H>,
81
+ hi: AsyncOrSync<Awaited<H>, R>
82
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
83
+ function pipeAsyncSideEffect<B, C, D, E, F, G, H, I, R>(
84
+ ab: ZeroFn<B>,
85
+ bc: AsyncOrSync<Awaited<B>, C>,
86
+ cd: AsyncOrSync<Awaited<C>, D>,
87
+ de: AsyncOrSync<Awaited<D>, E>,
88
+ ef: AsyncOrSync<Awaited<E>, F>,
89
+ fg: AsyncOrSync<Awaited<F>, G>,
90
+ gh: AsyncOrSync<Awaited<G>, H>,
91
+ hi: AsyncOrSync<Awaited<H>, I>,
92
+ ij: AsyncOrSync<Awaited<I>, R>
93
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
94
+ function pipeAsyncSideEffect<B, C, D, E, F, G, H, I, J, R>(
95
+ ab: ZeroFn<B>,
96
+ bc: AsyncOrSync<Awaited<B>, C>,
97
+ cd: AsyncOrSync<Awaited<C>, D>,
98
+ de: AsyncOrSync<Awaited<D>, E>,
99
+ ef: AsyncOrSync<Awaited<E>, F>,
100
+ fg: AsyncOrSync<Awaited<F>, G>,
101
+ gh: AsyncOrSync<Awaited<G>, H>,
102
+ hi: AsyncOrSync<Awaited<H>, I>,
103
+ ij: AsyncOrSync<Awaited<I>, J>,
104
+ jk: AsyncOrSync<Awaited<J>, R>
105
+ ): () => Promise<MaybeSideEffect<Awaited<R>>>;
32
106
  function pipeAsyncSideEffect<A, R>(
33
107
  ab: AsyncOrSync<A, R>
34
108
  ): (a: A | SideEffect<any>) => Promise<MaybeSideEffect<Awaited<R>>>;
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import compose from './compose';
3
+ import from from './from';
3
4
 
4
5
  describe('compose', () => {
5
6
  it('applies functions right-to-left', () => {
@@ -27,4 +28,17 @@ describe('compose', () => {
27
28
  expect(fn(4)).toBe(16);
28
29
  });
29
30
 
31
+ it('supports zero-arity tail functions', () => {
32
+ const addOne = (n: number) => n + 1;
33
+ const fn = compose(addOne, () => 2);
34
+ expect(fn()).toBe(3);
35
+ });
36
+
37
+ it('supports from() as a constant source', () => {
38
+ const addOne = (n: number) => n + 1;
39
+ const fn = compose(addOne, from(2));
40
+ expect(fn()).toBe(3);
41
+ expect(fn(10)).toBe(3);
42
+ });
43
+
30
44
  });
@@ -2,46 +2,158 @@
2
2
  * compose - 함수를 우→좌로 합성
3
3
  */
4
4
 
5
+ import type { FromFn } from './from';
6
+
5
7
  type UnaryFn<A, R> = (a: A) => R;
8
+ type ZeroFn<R> = () => R;
6
9
  type ComposeInput<Fns extends UnaryFn<any, any>[]> = Fns extends [...UnaryFn<any, any>[], UnaryFn<infer A, any>]
7
10
  ? A
8
11
  : never;
9
- type ComposeChain<Fns extends UnaryFn<any, any>[]> = Fns extends [UnaryFn<infer A, infer R>]
10
- ? UnaryFn<A, R>
12
+ type ComposeOutput<Fns extends UnaryFn<any, any>[]> = Fns extends [UnaryFn<any, infer R>]
13
+ ? R
11
14
  : Fns extends [UnaryFn<infer A, infer R>, ...infer Rest]
12
- ? Rest extends [UnaryFn<R, any>, ...UnaryFn<any, any>[]]
13
- ? ComposeChain<Rest> extends UnaryFn<any, infer NextR>
14
- ? UnaryFn<A, NextR>
15
+ ? Rest extends [UnaryFn<any, any>, ...UnaryFn<any, any>[]]
16
+ ? ComposeOutput<Rest> extends A
17
+ ? R
15
18
  : never
16
19
  : never
17
20
  : never;
18
- type Compose<Fns extends UnaryFn<any, any>[]> = (input: ComposeInput<Fns>) => ComposeChain<Fns> extends UnaryFn<
19
- any,
20
- infer R
21
- >
22
- ? R
23
- : never;
21
+ type Compose<Fns extends UnaryFn<any, any>[]> = (input: ComposeInput<Fns>) => ComposeOutput<Fns>;
22
+ type ComposeFrom<Fns extends [...UnaryFn<any, any>[], FromFn<any>]> = (input?: ComposeInput<Fns>) => ComposeOutput<Fns>;
23
+
24
+ function compose<R>(ab: ZeroFn<R>): () => R;
25
+ function compose<B, R>(ab: UnaryFn<B, R>, bc: ZeroFn<B>): () => R;
26
+ function compose<C, B, R>(ab: UnaryFn<B, R>, bc: UnaryFn<C, B>, cd: ZeroFn<C>): () => R;
27
+ function compose<D, C, B, R>(ab: UnaryFn<B, R>, bc: UnaryFn<C, B>, cd: UnaryFn<D, C>, de: ZeroFn<D>): () => R;
28
+ function compose<E, D, C, B, R>(
29
+ ab: UnaryFn<B, R>,
30
+ bc: UnaryFn<C, B>,
31
+ cd: UnaryFn<D, C>,
32
+ de: UnaryFn<E, D>,
33
+ ef: ZeroFn<E>
34
+ ): () => R;
35
+ function compose<F, E, D, C, B, R>(
36
+ ab: UnaryFn<B, R>,
37
+ bc: UnaryFn<C, B>,
38
+ cd: UnaryFn<D, C>,
39
+ de: UnaryFn<E, D>,
40
+ ef: UnaryFn<F, E>,
41
+ fg: ZeroFn<F>
42
+ ): () => R;
43
+ function compose<G, F, E, D, C, B, R>(
44
+ ab: UnaryFn<B, R>,
45
+ bc: UnaryFn<C, B>,
46
+ cd: UnaryFn<D, C>,
47
+ de: UnaryFn<E, D>,
48
+ ef: UnaryFn<F, E>,
49
+ fg: UnaryFn<G, F>,
50
+ gh: ZeroFn<G>
51
+ ): () => R;
52
+ function compose<H, G, F, E, D, C, B, R>(
53
+ ab: UnaryFn<B, R>,
54
+ bc: UnaryFn<C, B>,
55
+ cd: UnaryFn<D, C>,
56
+ de: UnaryFn<E, D>,
57
+ ef: UnaryFn<F, E>,
58
+ fg: UnaryFn<G, F>,
59
+ gh: UnaryFn<H, G>,
60
+ hi: ZeroFn<H>
61
+ ): () => R;
62
+ function compose<I, H, G, F, E, D, C, B, R>(
63
+ ab: UnaryFn<B, R>,
64
+ bc: UnaryFn<C, B>,
65
+ cd: UnaryFn<D, C>,
66
+ de: UnaryFn<E, D>,
67
+ ef: UnaryFn<F, E>,
68
+ fg: UnaryFn<G, F>,
69
+ gh: UnaryFn<H, G>,
70
+ hi: UnaryFn<I, H>,
71
+ ij: ZeroFn<I>
72
+ ): () => R;
73
+ function compose<J, I, H, G, F, E, D, C, B, R>(
74
+ ab: UnaryFn<B, R>,
75
+ bc: UnaryFn<C, B>,
76
+ cd: UnaryFn<D, C>,
77
+ de: UnaryFn<E, D>,
78
+ ef: UnaryFn<F, E>,
79
+ fg: UnaryFn<G, F>,
80
+ gh: UnaryFn<H, G>,
81
+ hi: UnaryFn<I, H>,
82
+ ij: UnaryFn<J, I>,
83
+ jk: ZeroFn<J>
84
+ ): () => R;
24
85
 
86
+ function compose<Fns extends [...UnaryFn<any, any>[], FromFn<any>]>(...funcs: Fns): ComposeFrom<Fns>;
25
87
  function compose<A, R>(ab: UnaryFn<A, R>): (a: A) => R;
26
- function compose<A, B, R>(ab: UnaryFn<A, B>, bc: UnaryFn<B, R>): (a: A) => R;
27
- function compose<A, B, C, R>(ab: UnaryFn<A, B>, bc: UnaryFn<B, C>, cd: UnaryFn<C, R>): (a: A) => R;
88
+ function compose<A, B, R>(ab: UnaryFn<B, R>, bc: UnaryFn<A, B>): (a: A) => R;
89
+ function compose<A, B, C, R>(ab: UnaryFn<C, R>, bc: UnaryFn<B, C>, cd: UnaryFn<A, B>): (a: A) => R;
28
90
  function compose<A, B, C, D, R>(
29
- ab: UnaryFn<A, B>,
30
- bc: UnaryFn<B, C>,
31
- cd: UnaryFn<C, D>,
32
- de: UnaryFn<D, R>
91
+ ab: UnaryFn<D, R>,
92
+ bc: UnaryFn<C, D>,
93
+ cd: UnaryFn<B, C>,
94
+ de: UnaryFn<A, B>
33
95
  ): (a: A) => R;
34
96
  function compose<A, B, C, D, E, R>(
35
- ab: UnaryFn<A, B>,
36
- bc: UnaryFn<B, C>,
97
+ ab: UnaryFn<E, R>,
98
+ bc: UnaryFn<D, E>,
37
99
  cd: UnaryFn<C, D>,
100
+ de: UnaryFn<B, C>,
101
+ ef: UnaryFn<A, B>
102
+ ): (a: A) => R;
103
+ function compose<A, B, C, D, E, F, R>(
104
+ ab: UnaryFn<F, R>,
105
+ bc: UnaryFn<E, F>,
106
+ cd: UnaryFn<D, E>,
107
+ de: UnaryFn<C, D>,
108
+ ef: UnaryFn<B, C>,
109
+ fg: UnaryFn<A, B>
110
+ ): (a: A) => R;
111
+ function compose<A, B, C, D, E, F, G, R>(
112
+ ab: UnaryFn<G, R>,
113
+ bc: UnaryFn<F, G>,
114
+ cd: UnaryFn<E, F>,
38
115
  de: UnaryFn<D, E>,
39
- ef: UnaryFn<E, R>
116
+ ef: UnaryFn<C, D>,
117
+ fg: UnaryFn<B, C>,
118
+ gh: UnaryFn<A, B>
119
+ ): (a: A) => R;
120
+ function compose<A, B, C, D, E, F, G, H, R>(
121
+ ab: UnaryFn<H, R>,
122
+ bc: UnaryFn<G, H>,
123
+ cd: UnaryFn<F, G>,
124
+ de: UnaryFn<E, F>,
125
+ ef: UnaryFn<D, E>,
126
+ fg: UnaryFn<C, D>,
127
+ gh: UnaryFn<B, C>,
128
+ hi: UnaryFn<A, B>
129
+ ): (a: A) => R;
130
+ function compose<A, B, C, D, E, F, G, H, I, R>(
131
+ ab: UnaryFn<I, R>,
132
+ bc: UnaryFn<H, I>,
133
+ cd: UnaryFn<G, H>,
134
+ de: UnaryFn<F, G>,
135
+ ef: UnaryFn<E, F>,
136
+ fg: UnaryFn<D, E>,
137
+ gh: UnaryFn<C, D>,
138
+ hi: UnaryFn<B, C>,
139
+ ij: UnaryFn<A, B>
140
+ ): (a: A) => R;
141
+ function compose<A, B, C, D, E, F, G, H, I, J, R>(
142
+ ab: UnaryFn<J, R>,
143
+ bc: UnaryFn<I, J>,
144
+ cd: UnaryFn<H, I>,
145
+ de: UnaryFn<G, H>,
146
+ ef: UnaryFn<F, G>,
147
+ fg: UnaryFn<E, F>,
148
+ gh: UnaryFn<D, E>,
149
+ hi: UnaryFn<C, D>,
150
+ ij: UnaryFn<B, C>,
151
+ jk: UnaryFn<A, B>
40
152
  ): (a: A) => R;
41
153
 
42
154
  function compose<Fns extends [UnaryFn<any, any>, ...UnaryFn<any, any>[]]>(...funcs: Fns): Compose<Fns>;
43
155
  function compose(...funcs: Array<UnaryFn<any, any>>): (input: any) => any;
44
- function compose(...funcs: Array<(input: any) => any>) {
156
+ function compose(...funcs: Array<(...args: any[]) => any>) {
45
157
  return (value: any) => funcs.reduceRight((acc, fn) => fn(acc), value);
46
158
  }
47
159