@satoshibits/functional 1.0.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.
- package/README.md +242 -0
- package/dist/array-utils.d.mts +317 -0
- package/dist/array-utils.d.mts.map +1 -0
- package/dist/array-utils.mjs +370 -0
- package/dist/array-utils.mjs.map +1 -0
- package/dist/composition.d.mts +603 -0
- package/dist/composition.d.mts.map +1 -0
- package/dist/composition.mjs +516 -0
- package/dist/composition.mjs.map +1 -0
- package/dist/object-utils.d.mts +267 -0
- package/dist/object-utils.d.mts.map +1 -0
- package/dist/object-utils.mjs +258 -0
- package/dist/object-utils.mjs.map +1 -0
- package/dist/option.d.mts +622 -0
- package/dist/option.d.mts.map +1 -0
- package/dist/option.mjs +637 -0
- package/dist/option.mjs.map +1 -0
- package/dist/performance.d.mts +265 -0
- package/dist/performance.d.mts.map +1 -0
- package/dist/performance.mjs +453 -0
- package/dist/performance.mjs.map +1 -0
- package/dist/pipeline.d.mts +431 -0
- package/dist/pipeline.d.mts.map +1 -0
- package/dist/pipeline.mjs +460 -0
- package/dist/pipeline.mjs.map +1 -0
- package/dist/predicates.d.mts +722 -0
- package/dist/predicates.d.mts.map +1 -0
- package/dist/predicates.mjs +802 -0
- package/dist/predicates.mjs.map +1 -0
- package/dist/reader-result.d.mts +422 -0
- package/dist/reader-result.d.mts.map +1 -0
- package/dist/reader-result.mjs +758 -0
- package/dist/reader-result.mjs.map +1 -0
- package/dist/result.d.mts +684 -0
- package/dist/result.d.mts.map +1 -0
- package/dist/result.mjs +814 -0
- package/dist/result.mjs.map +1 -0
- package/dist/types.d.mts +439 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +191 -0
- package/dist/types.mjs.map +1 -0
- package/dist/validation.d.mts +622 -0
- package/dist/validation.d.mts.map +1 -0
- package/dist/validation.mjs +852 -0
- package/dist/validation.mjs.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module composition
|
|
4
|
+
* @description Core functional composition utilities using TypeScript 4.0+ variadic tuple types.
|
|
5
|
+
* Provides type-safe function composition with automatic type inference
|
|
6
|
+
* for pipelines of any length. All functions follow functional programming principles,
|
|
7
|
+
* supporting both synchronous and asynchronous composition patterns.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { pipe, flow, compose, tap, pipeAsync } from './composition.mts';
|
|
12
|
+
*
|
|
13
|
+
* // pipe - execute functions with an initial value
|
|
14
|
+
* const result = pipe(
|
|
15
|
+
* 5,
|
|
16
|
+
* x => x * 2,
|
|
17
|
+
* x => x + 1,
|
|
18
|
+
* x => `Result: ${x}`
|
|
19
|
+
* );
|
|
20
|
+
* // => "Result: 11"
|
|
21
|
+
*
|
|
22
|
+
* // flow - create reusable pipelines
|
|
23
|
+
* const processUser = flow(
|
|
24
|
+
* (user: User) => ({ ...user, name: user.name.trim() }),
|
|
25
|
+
* user => ({ ...user, name: user.name.toUpperCase() }),
|
|
26
|
+
* user => ({ ...user, isActive: true })
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* // compose - right-to-left composition
|
|
30
|
+
* const calculate = compose(
|
|
31
|
+
* Math.round,
|
|
32
|
+
* Math.sqrt,
|
|
33
|
+
* Math.abs
|
|
34
|
+
* );
|
|
35
|
+
*
|
|
36
|
+
* // async composition
|
|
37
|
+
* const fetchAndProcess = pipeAsync(
|
|
38
|
+
* async (id: string) => fetchUser(id),
|
|
39
|
+
* async (user: User) => enrichUser(user),
|
|
40
|
+
* async (enriched: EnrichedUser) => saveUser(enriched)
|
|
41
|
+
* );
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @category Core
|
|
45
|
+
* @since 2025-07-03
|
|
46
|
+
*/
|
|
47
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
48
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
49
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
50
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
51
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
52
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
53
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
57
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
58
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
59
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
60
|
+
function step(op) {
|
|
61
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
62
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
63
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
64
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
65
|
+
switch (op[0]) {
|
|
66
|
+
case 0: case 1: t = op; break;
|
|
67
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
68
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
69
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
70
|
+
default:
|
|
71
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
72
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
73
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
74
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
75
|
+
if (t[2]) _.ops.pop();
|
|
76
|
+
_.trys.pop(); continue;
|
|
77
|
+
}
|
|
78
|
+
op = body.call(thisArg, _);
|
|
79
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
80
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
84
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
85
|
+
if (ar || !(i in from)) {
|
|
86
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
87
|
+
ar[i] = from[i];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
91
|
+
};
|
|
92
|
+
// General case for more functions (less type safety but still works)
|
|
93
|
+
export function pipe(value) {
|
|
94
|
+
var fns = [];
|
|
95
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
96
|
+
fns[_i - 1] = arguments[_i];
|
|
97
|
+
}
|
|
98
|
+
return fns.reduce(function (acc, fn) { return fn(acc); }, value);
|
|
99
|
+
}
|
|
100
|
+
export function flow() {
|
|
101
|
+
var fns = [];
|
|
102
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
103
|
+
fns[_i] = arguments[_i];
|
|
104
|
+
}
|
|
105
|
+
if (fns.length === 0)
|
|
106
|
+
return identity;
|
|
107
|
+
if (fns.length === 1)
|
|
108
|
+
return fns[0];
|
|
109
|
+
var firstFn = fns[0], restFns = fns.slice(1);
|
|
110
|
+
return function () {
|
|
111
|
+
var args = [];
|
|
112
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
113
|
+
args[_i] = arguments[_i];
|
|
114
|
+
}
|
|
115
|
+
var firstResult = firstFn.apply(void 0, args);
|
|
116
|
+
return restFns.reduce(function (acc, fn) { return fn(acc); }, firstResult);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Identity function - returns its input unchanged.
|
|
121
|
+
* @description A function that returns its argument without any modification.
|
|
122
|
+
* Useful as a default or placeholder in pipelines, for filtering truthy values,
|
|
123
|
+
* or when conditional transformations are needed.
|
|
124
|
+
*
|
|
125
|
+
* @template T - The type of the input and output
|
|
126
|
+
* @param {T} x - The value to return unchanged
|
|
127
|
+
* @returns {T} The same value that was passed in
|
|
128
|
+
*
|
|
129
|
+
* @category Utilities
|
|
130
|
+
* @example
|
|
131
|
+
* // As a default transformation
|
|
132
|
+
* const transform = shouldTransform ? (x: number) => x * 2 : identity;
|
|
133
|
+
* [1, 2, 3].map(transform); // => [1, 2, 3] or [2, 4, 6]
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* // Filtering out falsy values while preserving types
|
|
137
|
+
* const values = [1, null, 2, undefined, 3];
|
|
138
|
+
* const nonNull = values.filter(identity); // => [1, 2, 3]
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* // As a placeholder in conditional pipelines
|
|
142
|
+
* const pipeline = flow(
|
|
143
|
+
* validateInput,
|
|
144
|
+
* shouldNormalize ? normalizeData : identity,
|
|
145
|
+
* saveToDatabase
|
|
146
|
+
* );
|
|
147
|
+
*
|
|
148
|
+
* @see constant - Create a function that always returns the same value
|
|
149
|
+
* @since 2025-07-03
|
|
150
|
+
*/
|
|
151
|
+
export var identity = function (x) { return x; };
|
|
152
|
+
/**
|
|
153
|
+
* Constant function - creates a function that always returns the same value.
|
|
154
|
+
* @description Returns a function that ignores its arguments and always returns the specified value.
|
|
155
|
+
* Useful for providing default values, mocking in tests, or replacing complex logic with fixed results.
|
|
156
|
+
*
|
|
157
|
+
* @template T - The type of the constant value
|
|
158
|
+
* @param {T} x - The value to always return
|
|
159
|
+
* @returns {() => T} A function that always returns the constant value
|
|
160
|
+
*
|
|
161
|
+
* @category Utilities
|
|
162
|
+
* @example
|
|
163
|
+
* // Default value provider
|
|
164
|
+
* const getDefault = constant({ status: 'pending', count: 0 });
|
|
165
|
+
* const status = userStatus || getDefault();
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* // Mock functions in tests
|
|
169
|
+
* const mockUserService = {
|
|
170
|
+
* getCurrentUser: constant({ id: 1, name: 'Test User' }),
|
|
171
|
+
* isAuthenticated: constant(true)
|
|
172
|
+
* };
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* // Replacing conditional logic
|
|
176
|
+
* const getDiscount = isPremium
|
|
177
|
+
* ? calculatePremiumDiscount
|
|
178
|
+
* : constant(0);
|
|
179
|
+
*
|
|
180
|
+
* @see identity - Return the input unchanged
|
|
181
|
+
* @since 2025-07-03
|
|
182
|
+
*/
|
|
183
|
+
export var constant = function (x) { return function () { return x; }; };
|
|
184
|
+
export function compose() {
|
|
185
|
+
var fns = [];
|
|
186
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
187
|
+
fns[_i] = arguments[_i];
|
|
188
|
+
}
|
|
189
|
+
// Create a reversed copy to avoid mutation
|
|
190
|
+
var reversedFns = __spreadArray([], fns, true).reverse();
|
|
191
|
+
return flow.apply(void 0, reversedFns);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Tap - execute a side effect without changing the value.
|
|
195
|
+
* @description Executes a function for its side effects while passing the input value through unchanged.
|
|
196
|
+
* Useful for debugging, logging, or triggering external actions in pipelines without breaking the flow.
|
|
197
|
+
* The side effect function receives the value but its return value is ignored.
|
|
198
|
+
*
|
|
199
|
+
* @template T - The type of the value being passed through
|
|
200
|
+
* @param {function(T): void} fn - Function to execute for side effects
|
|
201
|
+
* @returns {function(T): T} A function that executes the side effect and returns the input
|
|
202
|
+
*
|
|
203
|
+
* @category Side Effects
|
|
204
|
+
* @example
|
|
205
|
+
* // Debugging pipeline steps
|
|
206
|
+
* const result = pipe(
|
|
207
|
+
* { name: 'John', age: 30 },
|
|
208
|
+
* tap(console.log), // Log initial value
|
|
209
|
+
* user => ({ ...user, age: user.age + 1 }),
|
|
210
|
+
* tap(user => console.log('After increment:', user)),
|
|
211
|
+
* user => ({ ...user, status: 'active' })
|
|
212
|
+
* );
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* // Triggering side effects
|
|
216
|
+
* const saveUser = pipe(
|
|
217
|
+
* validateUser,
|
|
218
|
+
* tap(user => analytics.track('user_validated', { id: user.id })),
|
|
219
|
+
* normalizeUser,
|
|
220
|
+
* tap(user => cache.set(user.id, user)),
|
|
221
|
+
* saveToDatabase
|
|
222
|
+
* );
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* // Conditional debugging
|
|
226
|
+
* const debug = process.env.DEBUG === 'true';
|
|
227
|
+
* const pipeline = flow(
|
|
228
|
+
* parseData,
|
|
229
|
+
* debug ? tap(data => console.log('Parsed:', data)) : identity,
|
|
230
|
+
* transformData
|
|
231
|
+
* );
|
|
232
|
+
*
|
|
233
|
+
* @see identity - Pass through without side effects
|
|
234
|
+
* @since 2025-07-03
|
|
235
|
+
*/
|
|
236
|
+
export var tap = function (fn) { return function (x) {
|
|
237
|
+
fn(x);
|
|
238
|
+
return x;
|
|
239
|
+
}; };
|
|
240
|
+
/**
|
|
241
|
+
* Currying utility - converts a function of multiple arguments into a sequence of functions.
|
|
242
|
+
* Each function takes a single argument and returns another function until all arguments are provided.
|
|
243
|
+
*
|
|
244
|
+
* @category Function Transformation
|
|
245
|
+
* @example
|
|
246
|
+
* // Basic currying
|
|
247
|
+
* const add = (a: number, b: number) => a + b;
|
|
248
|
+
* const curriedAdd = curry(add);
|
|
249
|
+
* const add5 = curriedAdd(5);
|
|
250
|
+
* add5(3); // => 8
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* // Building reusable functions
|
|
254
|
+
* const multiply = curry((factor: number, value: number) => value * factor);
|
|
255
|
+
* const double = multiply(2);
|
|
256
|
+
* const triple = multiply(3);
|
|
257
|
+
*
|
|
258
|
+
* [1, 2, 3].map(double); // => [2, 4, 6]
|
|
259
|
+
* [1, 2, 3].map(triple); // => [3, 6, 9]
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* // Configuration functions
|
|
263
|
+
* const createLogger = curry((level: string, category: string, message: string) =>
|
|
264
|
+
* console.log(`[${level}] ${category}: ${message}`)
|
|
265
|
+
* );
|
|
266
|
+
*
|
|
267
|
+
* const errorLogger = createLogger('ERROR');
|
|
268
|
+
* const authErrorLogger = errorLogger('AUTH');
|
|
269
|
+
* authErrorLogger('Invalid credentials'); // => "[ERROR] AUTH: Invalid credentials"
|
|
270
|
+
*
|
|
271
|
+
* @see partial - Fix some arguments of a function
|
|
272
|
+
* @see flip - Reverse argument order
|
|
273
|
+
*/
|
|
274
|
+
export var curry = function (fn) {
|
|
275
|
+
return function (a) {
|
|
276
|
+
return function (b) {
|
|
277
|
+
return fn(a, b);
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
/**
|
|
282
|
+
* Partial application - fixes some arguments of a function.
|
|
283
|
+
* Returns a new function that takes the remaining arguments.
|
|
284
|
+
*
|
|
285
|
+
* @category Function Transformation
|
|
286
|
+
* @example
|
|
287
|
+
* // Partially apply configuration
|
|
288
|
+
* const greet = (greeting: string, name: string) => `${greeting}, ${name}!`;
|
|
289
|
+
* const sayHello = partial(greet, 'Hello');
|
|
290
|
+
* sayHello('World'); // => "Hello, World!"
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* // Creating specialized functions
|
|
294
|
+
* const fetchAPI = (method: string, endpoint: string, body?: unknown) =>
|
|
295
|
+
* fetch(endpoint, { method, body: JSON.stringify(body) });
|
|
296
|
+
*
|
|
297
|
+
* const postAPI = partial(fetchAPI, 'POST');
|
|
298
|
+
* const getAPI = partial(fetchAPI, 'GET');
|
|
299
|
+
*
|
|
300
|
+
* postAPI('/users', { name: 'John' });
|
|
301
|
+
* getAPI('/users');
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* // Event handler specialization
|
|
305
|
+
* const logEvent = (category: string, action: string, label: string) =>
|
|
306
|
+
* analytics.track({ category, action, label });
|
|
307
|
+
*
|
|
308
|
+
* const logUserAction = partial(logEvent, 'USER');
|
|
309
|
+
* const logButtonClick = partial(logUserAction, 'CLICK');
|
|
310
|
+
*
|
|
311
|
+
* logButtonClick('submit-form'); // Logs: { category: 'USER', action: 'CLICK', label: 'submit-form' }
|
|
312
|
+
*
|
|
313
|
+
* @see curry - Convert to single-argument functions
|
|
314
|
+
* @see flip - Reverse argument order
|
|
315
|
+
*/
|
|
316
|
+
export var partial = function (fn) {
|
|
317
|
+
var args = [];
|
|
318
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
319
|
+
args[_i - 1] = arguments[_i];
|
|
320
|
+
}
|
|
321
|
+
return function (lastArg) {
|
|
322
|
+
return fn.apply(void 0, __spreadArray(__spreadArray([], args, false), [lastArg], false));
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
/**
|
|
326
|
+
* Flip - reverses the order of arguments for a binary function.
|
|
327
|
+
* Useful when you need to adapt a function to work with different argument orders.
|
|
328
|
+
*
|
|
329
|
+
* @category Function Transformation
|
|
330
|
+
* @example
|
|
331
|
+
* // Basic flip
|
|
332
|
+
* const divide = (a: number, b: number) => a / b;
|
|
333
|
+
* const divideBy = flip(divide);
|
|
334
|
+
* divideBy(2, 10); // => 5 (10 / 2)
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* // Adapting functions for composition
|
|
338
|
+
* const concat = (a: string, b: string) => a + b;
|
|
339
|
+
* const prepend = flip(concat);
|
|
340
|
+
*
|
|
341
|
+
* const addPrefix = prepend('PREFIX_');
|
|
342
|
+
* addPrefix('value'); // => "valuePREFIX_"
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* // Working with collections
|
|
346
|
+
* const has = (obj: Record<string, unknown>, key: string) => key in obj;
|
|
347
|
+
* const hasKey = flip(has);
|
|
348
|
+
*
|
|
349
|
+
* const users = [{ name: 'John' }, { name: 'Jane', admin: true }];
|
|
350
|
+
* users.filter(hasKey('admin')); // => [{ name: 'Jane', admin: true }]
|
|
351
|
+
*
|
|
352
|
+
* @see curry - Convert to single-argument functions
|
|
353
|
+
* @see partial - Fix some arguments
|
|
354
|
+
*/
|
|
355
|
+
export var flip = function (fn) {
|
|
356
|
+
return function (b, a) {
|
|
357
|
+
return fn(a, b);
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* Memoization - caches function results based on arguments.
|
|
362
|
+
* Improves performance for expensive pure functions by storing previously computed results.
|
|
363
|
+
*
|
|
364
|
+
* @category Performance
|
|
365
|
+
* @example
|
|
366
|
+
* // Memoize expensive calculations
|
|
367
|
+
* const fibonacci = memoize((n: number): number => {
|
|
368
|
+
* if (n <= 1) return n;
|
|
369
|
+
* return fibonacci(n - 1) + fibonacci(n - 2);
|
|
370
|
+
* });
|
|
371
|
+
*
|
|
372
|
+
* fibonacci(40); // First call: slow
|
|
373
|
+
* fibonacci(40); // Second call: instant (cached)
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* // Custom cache key generation
|
|
377
|
+
* const processUser = memoize(
|
|
378
|
+
* async (userId: string, options: { includeDetails: boolean }) => {
|
|
379
|
+
* const user = await fetchUser(userId);
|
|
380
|
+
* return options.includeDetails
|
|
381
|
+
* ? { ...user, details: await fetchUserDetails(userId) }
|
|
382
|
+
* : user;
|
|
383
|
+
* },
|
|
384
|
+
* (userId, options) => `${userId}-${options.includeDetails}`
|
|
385
|
+
* );
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* // Memoizing API calls
|
|
389
|
+
* const fetchProductData = memoize(async (productId: string) => {
|
|
390
|
+
* const response = await fetch(`/api/products/${productId}`);
|
|
391
|
+
* return response.json();
|
|
392
|
+
* });
|
|
393
|
+
*
|
|
394
|
+
* // Multiple components can call this without duplicate requests
|
|
395
|
+
* await fetchProductData('123'); // Makes API call
|
|
396
|
+
* await fetchProductData('123'); // Returns cached result
|
|
397
|
+
*
|
|
398
|
+
* @see identity - For functions that don't need memoization
|
|
399
|
+
*/
|
|
400
|
+
export var memoize = function (fn, getKey) {
|
|
401
|
+
var cache = new Map();
|
|
402
|
+
var keyFn = getKey !== null && getKey !== void 0 ? getKey : (function () {
|
|
403
|
+
var args = [];
|
|
404
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
405
|
+
args[_i] = arguments[_i];
|
|
406
|
+
}
|
|
407
|
+
return JSON.stringify(args);
|
|
408
|
+
});
|
|
409
|
+
return function () {
|
|
410
|
+
var args = [];
|
|
411
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
412
|
+
args[_i] = arguments[_i];
|
|
413
|
+
}
|
|
414
|
+
var key = keyFn.apply(void 0, args);
|
|
415
|
+
if (cache.has(key)) {
|
|
416
|
+
return cache.get(key);
|
|
417
|
+
}
|
|
418
|
+
var result = fn.apply(void 0, args);
|
|
419
|
+
cache.set(key, result);
|
|
420
|
+
return result;
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
export function composeAsync() {
|
|
424
|
+
var fns = [];
|
|
425
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
426
|
+
fns[_i] = arguments[_i];
|
|
427
|
+
}
|
|
428
|
+
if (fns.length === 0) {
|
|
429
|
+
return function (x) { return Promise.resolve(x); };
|
|
430
|
+
}
|
|
431
|
+
if (fns.length === 1) {
|
|
432
|
+
return fns[0];
|
|
433
|
+
}
|
|
434
|
+
return function (initial) {
|
|
435
|
+
// Process functions from right to left
|
|
436
|
+
return fns.reduceRight(function (acc, fn) { return acc.then(function (result) { return fn(result); }); }, Promise.resolve(initial));
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
export function pipeAsync() {
|
|
440
|
+
var fns = [];
|
|
441
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
442
|
+
fns[_i] = arguments[_i];
|
|
443
|
+
}
|
|
444
|
+
if (fns.length === 0) {
|
|
445
|
+
return function (x) { return Promise.resolve(x); };
|
|
446
|
+
}
|
|
447
|
+
if (fns.length === 1) {
|
|
448
|
+
return fns[0];
|
|
449
|
+
}
|
|
450
|
+
return function (initial) {
|
|
451
|
+
// Process functions from left to right
|
|
452
|
+
return fns.reduce(function (acc, fn) { return acc.then(function (result) { return fn(result); }); }, Promise.resolve(initial));
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Sequential execution of async functions.
|
|
457
|
+
* @description Executes an array of async functions in order, waiting for each to complete before starting the next.
|
|
458
|
+
* Collects and returns all results in an array. Unlike Promise.all, this ensures sequential execution
|
|
459
|
+
* which is useful when operations depend on each other or when you need to limit concurrency.
|
|
460
|
+
*
|
|
461
|
+
* @template T - The type of values returned by the async functions
|
|
462
|
+
* @param {Array<() => Promise<T>>} fns - Array of async functions to execute
|
|
463
|
+
* @returns {Promise<T[]>} Promise resolving to array of all results in order
|
|
464
|
+
*
|
|
465
|
+
* @category Async
|
|
466
|
+
* @example
|
|
467
|
+
* // Execute initialization steps in order
|
|
468
|
+
* const initSteps = [
|
|
469
|
+
* async () => connectDatabase(),
|
|
470
|
+
* async () => loadConfiguration(),
|
|
471
|
+
* async () => startServer()
|
|
472
|
+
* ];
|
|
473
|
+
*
|
|
474
|
+
* const results = await sequence(initSteps);
|
|
475
|
+
* // All steps completed in order
|
|
476
|
+
*
|
|
477
|
+
* @example
|
|
478
|
+
* // Data fetching sequence
|
|
479
|
+
* const fetchOperations = userIds.map(id =>
|
|
480
|
+
* async () => fetchUserData(id)
|
|
481
|
+
* );
|
|
482
|
+
*
|
|
483
|
+
* const allUserData = await sequence(fetchOperations);
|
|
484
|
+
* // Fetches users one by one, not in parallel
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* // Cleanup operations
|
|
488
|
+
* const cleanup = sequence([
|
|
489
|
+
* async () => closeConnections(),
|
|
490
|
+
* async () => flushCache(),
|
|
491
|
+
* async () => logShutdown()
|
|
492
|
+
* ]);
|
|
493
|
+
*
|
|
494
|
+
* process.on('SIGTERM', () => cleanup());
|
|
495
|
+
*
|
|
496
|
+
* @see Promise.all - For parallel execution
|
|
497
|
+
* @see pipeAsync - For composing async functions
|
|
498
|
+
* @since 2025-07-03
|
|
499
|
+
*/
|
|
500
|
+
export var sequence = function (fns) {
|
|
501
|
+
return fns.reduce(function (promiseChain, currentFn) { return __awaiter(void 0, void 0, void 0, function () {
|
|
502
|
+
var chainResults, currentResult;
|
|
503
|
+
return __generator(this, function (_a) {
|
|
504
|
+
switch (_a.label) {
|
|
505
|
+
case 0: return [4 /*yield*/, promiseChain];
|
|
506
|
+
case 1:
|
|
507
|
+
chainResults = _a.sent();
|
|
508
|
+
return [4 /*yield*/, currentFn()];
|
|
509
|
+
case 2:
|
|
510
|
+
currentResult = _a.sent();
|
|
511
|
+
return [2 /*return*/, __spreadArray(__spreadArray([], chainResults, true), [currentResult], false)];
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
}); }, Promise.resolve([]));
|
|
515
|
+
};
|
|
516
|
+
//# sourceMappingURL=composition.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composition.mjs","sourceRoot":"","sources":["../src/composition.mts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEH,qEAAqE;AACrE,MAAM,UAAU,IAAI,CAAC,KAAc;IAAE,aAAqC;SAArC,UAAqC,EAArC,qBAAqC,EAArC,IAAqC;QAArC,4BAAqC;;IACxE,OAAO,GAAG,CAAC,MAAM,CAAC,UAAC,GAAG,EAAE,EAAE,IAAK,OAAA,EAAE,CAAC,GAAG,CAAC,EAAP,CAAO,EAAE,KAAK,CAAC,CAAC;AACjD,CAAC;AA2DD,MAAM,UAAU,IAAI;IAAC,aAAqC;SAArC,UAAqC,EAArC,qBAAqC,EAArC,IAAqC;QAArC,wBAAqC;;IACxD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACtC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;IAE7B,IAAA,OAAO,GAAgB,GAAG,GAAnB,EAAK,OAAO,GAAI,GAAG,SAAP,CAAQ;IAClC,OAAO;QAAC,cAAkB;aAAlB,UAAkB,EAAlB,qBAAkB,EAAlB,IAAkB;YAAlB,yBAAkB;;QACxB,IAAM,WAAW,GAAI,OAA2C,eAAI,IAAI,CAAC,CAAC;QAC1E,OAAO,OAAO,CAAC,MAAM,CAAC,UAAC,GAAG,EAAE,EAAE,IAAK,OAAA,EAAE,CAAC,GAAG,CAAC,EAAP,CAAO,EAAE,WAAW,CAAC,CAAC;IAC3D,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,IAAM,QAAQ,GAAG,UAAK,CAAI,IAAQ,OAAA,CAAC,EAAD,CAAC,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,IAAM,QAAQ,GAAG,UAAK,CAAI,IAAK,OAAA,cAAS,OAAA,CAAC,EAAD,CAAC,EAAV,CAAU,CAAC;AAwCjD,MAAM,UAAU,OAAO;IAAC,aAAqC;SAArC,UAAqC,EAArC,qBAAqC,EAArC,IAAqC;QAArC,wBAAqC;;IAC3D,2CAA2C;IAC3C,IAAM,WAAW,GAAG,kBAAI,GAAG,QAAE,OAAO,EAAE,CAAC;IACvC,OAAO,IAAI,eAAK,WAAuC,EAAE;AAC3D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,CAAC,IAAM,GAAG,GAAG,UAAK,EAAkB,IAAK,OAAA,UAAC,CAAI;IAClD,EAAE,CAAC,CAAC,CAAC,CAAC;IACN,OAAO,CAAC,CAAC;AACX,CAAC,EAH8C,CAG9C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,IAAM,KAAK,GAChB,UAAU,EAAqB;IAC/B,OAAA,UAAC,CAAI;QACL,OAAA,UAAC,CAAI;YACH,OAAA,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAAR,CAAQ;IADV,CACU;AAFV,CAEU,CAAC;AAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,IAAM,OAAO,GAClB,UAA4B,EAA6B;IAAE,cAAU;SAAV,UAAU,EAAV,qBAAU,EAAV,IAAU;QAAV,6BAAU;;IACrE,OAAA,UAAC,OAAU;QACT,OAAA,EAAE,+CAAI,IAAI,WAAE,OAAO;IAAnB,CAAoB;AADtB,CACsB,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,IAAM,IAAI,GACf,UAAU,EAAqB;IAC/B,OAAA,UAAC,CAAI,EAAE,CAAI;QACT,OAAA,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAAR,CAAQ;AADV,CACU,CAAC;AAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,CAAC,IAAM,OAAO,GAAG,UACrB,EAA6B,EAC7B,MAAkC;IAElC,IAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,IAAM,KAAK,GAAG,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,CAAC;QAAC,cAAa;aAAb,UAAa,EAAb,qBAAa,EAAb,IAAa;YAAb,yBAAa;;QAAK,OAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAApB,CAAoB,CAAC,CAAC;IAElE,OAAO;QAAC,cAAa;aAAb,UAAa,EAAb,qBAAa,EAAb,IAAa;YAAb,yBAAa;;QACnB,IAAM,GAAG,GAAG,KAAK,eAAI,IAAI,CAAC,CAAC;QAE3B,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAEnB,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;QACzB,CAAC;QAED,IAAM,MAAM,GAAG,EAAE,eAAI,IAAI,CAAC,CAAC;QAC3B,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC,CAAC;AAuEF,MAAM,UAAU,YAAY;IAAI,aAAkC;SAAlC,UAAkC,EAAlC,qBAAkC,EAAlC,IAAkC;QAAlC,wBAAkC;;IAChE,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,UAAC,CAAI,IAAK,OAAA,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAlB,CAAkB,CAAC;IACtC,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;IAChB,CAAC;IAED,OAAO,UAAC,OAAU;QAChB,uCAAuC;QACvC,OAAO,GAAG,CAAC,WAAW,CACpB,UAAC,GAAG,EAAE,EAAE,IAAK,OAAA,GAAG,CAAC,IAAI,CAAC,UAAA,MAAM,IAAI,OAAA,EAAE,CAAC,MAAM,CAAC,EAAV,CAAU,CAAC,EAA9B,CAA8B,EAC3C,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CACzB,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAoFD,MAAM,UAAU,SAAS;IAAI,aAAkC;SAAlC,UAAkC,EAAlC,qBAAkC,EAAlC,IAAkC;QAAlC,wBAAkC;;IAC7D,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,UAAC,CAAI,IAAK,OAAA,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAlB,CAAkB,CAAC;IACtC,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;IAChB,CAAC;IAED,OAAO,UAAC,OAAU;QAChB,uCAAuC;QACvC,OAAO,GAAG,CAAC,MAAM,CACf,UAAC,GAAG,EAAE,EAAE,IAAK,OAAA,GAAG,CAAC,IAAI,CAAC,UAAA,MAAM,IAAI,OAAA,EAAE,CAAC,MAAM,CAAC,EAAV,CAAU,CAAC,EAA9B,CAA8B,EAC3C,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CACzB,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,CAAC,IAAM,QAAQ,GAAG,UAAK,GAAyB;IACpD,OAAA,GAAG,CAAC,MAAM,CACR,UAAO,YAAY,EAAE,SAAS;;;;wBACP,qBAAM,YAAY,EAAA;;oBAAjC,YAAY,GAAG,SAAkB;oBACjB,qBAAM,SAAS,EAAE,EAAA;;oBAAjC,aAAa,GAAG,SAAiB;oBACvC,sDAAW,YAAY,UAAE,aAAa,WAAE;;;SACzC,EACD,OAAO,CAAC,OAAO,CAAC,EAAS,CAAC,CAC3B;AAPD,CAOC,CAAC"}
|