serial-task 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 kasukabe tsumugi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,355 @@
1
+ # Serial Task
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+
5
+ > Put a list of functions in and get a composed task function. Similar to functional programming's compose (function composition), but with more fine-grained and precise control, and the generated task incurs almost no runtime overhead. ✨
6
+
7
+ **Note**: For async functions, use `createSerialTaskAsync` instead of `createSerialTask`. Both functions have the same API, but `createSerialTaskAsync` properly handles async/await and Promise-based functions.
8
+
9
+ For more awesome packages, check out [my homepage💛](https://baendlorel.github.io/?repoType=npm)
10
+
11
+ ## 📦 Installation
12
+
13
+ ```bash
14
+ npm install serial-task
15
+ ```
16
+
17
+ ```bash
18
+ pnpm add serial-task
19
+ ```
20
+
21
+ ## 🎯 Quick Start
22
+
23
+ > Note: For async functions(tasks/resultWrapper/conditions), use `createSerialTaskAsync` instead.
24
+
25
+ ```typescript
26
+ import { createSerialTask } from 'serial-task';
27
+
28
+ // Create a serial task with multiple functions
29
+ const mathTask = createSerialTask({
30
+ tasks: [
31
+ (x: number) => x + 1, // Step 1: add 1
32
+ (x: number) => x * 2, // Step 2: multiply by 2
33
+ (x: number) => x - 1, // Step 3: subtract 1
34
+ ],
35
+ });
36
+
37
+ const result = mathTask(5);
38
+ console.log(result.value); // 11 -> ((5 + 1) * 2) - 1 = 11
39
+ console.log(result.results); // [6, 12, 11]
40
+ ```
41
+
42
+ ## 🔄 Execution Flow
43
+
44
+ The following diagram shows how functions are called in each iteration of the loop:
45
+
46
+ <div style="text-align: center">
47
+ <img src="https://raw.githubusercontent.com/baendlorel/serial-task/main/assets/flow.svg" alt="Execution Flow" width="360" />
48
+ </div>
49
+
50
+ ## 📖 API Reference
51
+
52
+ ### createSerialTask(options) / createSerialTaskAsync(options)
53
+
54
+ Creates a sync/async serial task function.
55
+
56
+ #### Parameters
57
+
58
+ - **options**: `SerialTaskOptions<F>`
59
+ - **name?**: `string` - Name of the generated task function (default: `'kskbTask'`)
60
+ - **tasks**: `F[]` - Array of functions to be executed in order
61
+ - **breakCondition?**: `function` - Function that determines when to break the loop (default: `() => false`)
62
+ - **skipCondition?**: `function` - Function that determines when to skip a task (default: `() => false`)
63
+ - **resultWrapper?**: `function` - Function that transforms input between tasks, default(means the first task gets original args, subsequent tasks get the last return value):
64
+ ```ts
65
+ (_task: Fn, index: number, _tasks: Fn[], args: unknown[], lastReturn: unknown) =>
66
+ index === 0 ? args : [lastReturn];
67
+ ```
68
+
69
+ #### Returns
70
+
71
+ A function that executes the tasks in order and returns a `TaskReturn<R>` object:
72
+
73
+ ```typescript
74
+ interface TaskReturn<R> {
75
+ value: R; // Result of the last executed task
76
+ results: R[]; // All results (skipped tasks are undefined)
77
+ trivial: boolean; // True if tasks array was empty
78
+ breakAt: number; // Index where loop broke (-1 if not broken)
79
+ skipped: number[]; // Indices of skipped tasks
80
+ }
81
+ ```
82
+
83
+ ## 🎨 Usage Scenarios
84
+
85
+ ### Scenario 1: Function Composition Pipeline
86
+
87
+ Perfect for data transformation pipelines where you need to apply multiple transformations in sequence:
88
+
89
+ ```typescript
90
+ import { createSerialTask } from 'serial-task';
91
+
92
+ // Data processing pipeline
93
+ interface UserData {
94
+ name: string;
95
+ email: string;
96
+ age: number;
97
+ }
98
+
99
+ const processUser = createSerialTask({
100
+ name: 'userProcessor',
101
+ tasks: [
102
+ // Step 1: Validate input
103
+ (user: UserData) => {
104
+ if (!user.email.includes('@')) {
105
+ throw new Error('Invalid email');
106
+ }
107
+ return user;
108
+ },
109
+
110
+ // Step 2: Normalize data
111
+ (user: UserData) => ({
112
+ ...user,
113
+ name: user.name.trim().toLowerCase(),
114
+ email: user.email.toLowerCase(),
115
+ }),
116
+
117
+ // Step 3: Add computed fields
118
+ (user: UserData) => ({
119
+ ...user,
120
+ isAdult: user.age >= 18,
121
+ displayName: user.name.charAt(0).toUpperCase() + user.name.slice(1),
122
+ }),
123
+
124
+ // Step 4: Generate summary
125
+ (user: any) => ({
126
+ ...user,
127
+ summary: `${user.displayName} (${user.email}) - ${user.isAdult ? 'Adult' : 'Minor'}`,
128
+ }),
129
+ ],
130
+ });
131
+
132
+ const result = processUser({
133
+ name: ' John Doe ',
134
+ email: 'JOHN@EXAMPLE.COM',
135
+ age: 25,
136
+ });
137
+
138
+ console.log(result.value);
139
+ // Output: {
140
+ // name: 'john doe',
141
+ // email: 'john@example.com',
142
+ // age: 25,
143
+ // isAdult: true,
144
+ // displayName: 'John doe',
145
+ // summary: 'John doe (john@example.com) - Adult'
146
+ // }
147
+ ```
148
+
149
+ ### Scenario 2: Event Handler Chain with Conditional Logic
150
+
151
+ Great for building middleware-like handler chains with skip and break logic:
152
+
153
+ ```typescript
154
+ import { createSerialTask } from 'serial-task';
155
+
156
+ interface Request {
157
+ path: string;
158
+ method: string;
159
+ headers: Record<string, string>;
160
+ body?: any;
161
+ metadata?: Record<string, any>;
162
+ }
163
+
164
+ // HTTP request handler chain
165
+ const requestHandler = createSerialTask({
166
+ name: 'httpHandler',
167
+ tasks: [
168
+ // Handler 1: Authentication
169
+ (req: Request) => {
170
+ console.log('🔐 Authenticating request...');
171
+ return {
172
+ ...req,
173
+ metadata: { ...req.metadata, authenticated: true, userId: 'user123' },
174
+ };
175
+ },
176
+
177
+ // Handler 2: Rate limiting
178
+ (req: Request) => {
179
+ console.log('⏱️ Checking rate limits...');
180
+ return {
181
+ ...req,
182
+ metadata: { ...req.metadata, rateLimited: false },
183
+ };
184
+ },
185
+
186
+ // Handler 3: Input validation
187
+ (req: Request) => {
188
+ console.log('✅ Validating input...');
189
+ if (req.method === 'POST' && !req.body) {
190
+ throw new Error('Body required for POST requests');
191
+ }
192
+ return {
193
+ ...req,
194
+ metadata: { ...req.metadata, validated: true },
195
+ };
196
+ },
197
+
198
+ // Handler 4: Business logic
199
+ (req: Request) => {
200
+ console.log('🔄 Processing business logic...');
201
+ return {
202
+ ...req,
203
+ metadata: { ...req.metadata, processed: true, result: 'success' },
204
+ };
205
+ },
206
+
207
+ // Handler 5: Response formatting
208
+ (req: Request) => {
209
+ console.log('📤 Formatting response...');
210
+ return {
211
+ ...req,
212
+ metadata: { ...req.metadata, formatted: true },
213
+ };
214
+ },
215
+ ],
216
+
217
+ // Skip rate limiting for admin users
218
+ skipCondition: (task, index, tasks, args, lastReturn) => {
219
+ if (index === 1) {
220
+ // rate limiting handler
221
+ const req = lastReturn as Request;
222
+ return req.metadata?.userId === 'admin';
223
+ }
224
+ return false;
225
+ },
226
+
227
+ // Break early if user is not authenticated
228
+ breakCondition: (task, index, tasks, args, lastReturn) => {
229
+ if (index > 0) {
230
+ // after authentication
231
+ const req = lastReturn as Request;
232
+ return !req.metadata?.authenticated;
233
+ }
234
+ return false;
235
+ },
236
+
237
+ // Pass the result to the next handler
238
+ resultWrapper: (task, index, tasks, args, lastReturn) => {
239
+ if (index === 0) {
240
+ return args; // First handler gets original args
241
+ }
242
+ return [...args, lastReturn]; // Subsequent handlers get the result from previous
243
+ },
244
+ });
245
+
246
+ // Example usage
247
+ const request: Request = {
248
+ path: '/api/users',
249
+ method: 'GET',
250
+ headers: { Authorization: 'Bearer token123' },
251
+ metadata: {},
252
+ };
253
+
254
+ const result = requestHandler(request);
255
+
256
+ console.log('Final result:', result.value);
257
+ console.log('Skipped handlers:', result.skipped); // e.g., [1] if rate limiting was skipped
258
+ console.log('Broke at:', result.breakAt); // -1 if completed successfully
259
+
260
+ // Example output:
261
+ // 🔐 Authenticating request...
262
+ // ⏱️ Checking rate limits...
263
+ // ✅ Validating input...
264
+ // 🔄 Processing business logic...
265
+ // 📤 Formatting response...
266
+ // Final result: { ... processed request with all metadata ... }
267
+ // Skipped handlers: []
268
+ // Broke at: -1
269
+ ```
270
+
271
+ ## 🔧 Advanced Features
272
+
273
+ ### Conditional Execution
274
+
275
+ Control the flow of your task execution with powerful conditions:
276
+
277
+ ```typescript
278
+ const conditionalTask = createSerialTask({
279
+ tasks: [taskA, taskB, taskC, taskD],
280
+
281
+ // Skip tasks based on conditions
282
+ skipCondition: (task, index, tasks, args, lastReturn) => {
283
+ // Skip taskB if input is negative
284
+ if (index === 1 && args[0] < 0) return true;
285
+ return false;
286
+ },
287
+
288
+ // Break early if result exceeds threshold
289
+ breakCondition: (task, index, tasks, args, lastReturn) => {
290
+ return lastReturn > 100;
291
+ },
292
+ });
293
+ ```
294
+
295
+ ### Dynamic Task Arrays
296
+
297
+ Modify the task array during execution:
298
+
299
+ ```typescript
300
+ const dynamicTask = createSerialTask({
301
+ tasks: [initialTask],
302
+ resultWrapper: (task, index, taskArray, args, lastReturn) => {
303
+ // Add more tasks dynamically
304
+ if (index === 0 && someCondition) {
305
+ taskArray.push(additionalTask);
306
+ }
307
+ return index === 0 ? args : [lastReturn];
308
+ },
309
+ });
310
+ ```
311
+
312
+ ## 🔄 Async Support
313
+
314
+ For async functions, use `createSerialTaskAsync`:
315
+
316
+ ```typescript
317
+ import { createSerialTaskAsync } from 'serial-task';
318
+
319
+ const asyncTask = createSerialTaskAsync({
320
+ tasks: [
321
+ async (data) => await fetchUserData(data),
322
+ async (user) => await validateUser(user),
323
+ async (user) => await saveUser(user),
324
+ ],
325
+ });
326
+
327
+ const result = await asyncTask(inputData);
328
+ ```
329
+
330
+ ## 🎪 Error Handling
331
+
332
+ Tasks can throw errors, which will propagate up and stop execution:
333
+
334
+ ```typescript
335
+ const taskWithErrors = createSerialTask({
336
+ tasks: [
337
+ (x) => x + 1,
338
+ (x) => {
339
+ if (x > 10) throw new Error('Value too large!');
340
+ return x * 2;
341
+ },
342
+ (x) => x - 1,
343
+ ],
344
+ });
345
+
346
+ try {
347
+ const result = taskWithErrors(15);
348
+ } catch (error) {
349
+ console.error('Task failed:', error.message);
350
+ }
351
+ ```
352
+
353
+ ## 📄 License
354
+
355
+ MIT
@@ -0,0 +1,184 @@
1
+ /**
2
+ * ## Usage
3
+ * Use this when you have async functions in tasks, conditions or result wrapper
4
+ *
5
+ * Creates an async serial task function that executes a series of functions in order
6
+ * - all given functions(`options.tasks`) will be called in order
7
+ * - generated task function will have the same length as the first task function
8
+ * - you can appoint generated task function's name by `options.name`
9
+ * - **Strongly Recommended**: all task functions have same input type and output type
10
+ * - returned function.length will be the same as the first task function's length
11
+ * @param opts Options for creating a serial task, details in `SerialTaskOptions`
12
+ * @returns a funtcion that executes the tasks in order, returns `TaskReturn<OriginalReturn>`
13
+ *
14
+ * ## About
15
+ * @package SerialTask
16
+ * @author Kasukabe Tsumugi <futami16237@gmail.com>
17
+ * @version 1.0.0 (Last Update: 2025.08.22 17:05:02.346)
18
+ * @license MIT
19
+ * @link https://github.com/baendlorel/serial-task
20
+ * @description Put a list of functions in and get a composed task function. Similar to functional programming's compose (function composition), but with more fine-grained and precise control, and the generated task incurs almost no runtime overhead. Supports both synchronous and asynchronous functions.
21
+ * @copyright Copyright (c) 2025 Kasukabe Tsumugi. All rights reserved.
22
+ */
23
+ declare function createSerialTaskAsync<F extends Fn>(opts: SerialTaskOptions<F>): TaskifyAsync<F>;
24
+
25
+ /**
26
+ * ## Usage
27
+ * **DO NOT Use** this when you have **async functions** in tasks, conditions or result wrapper
28
+ *
29
+ * Creates a serial task function that executes a series of functions in order.
30
+ * - all given functions(`options.tasks`) will be called in order
31
+ * - the returned value will be `options.resultWrapper`ed then passed to the next task
32
+ * - generated task function will have the same length as the first task function
33
+ * - you can appoint generated task function's name by `options.name`
34
+ * - **Strongly Recommended**: all task functions have same input type and output type
35
+ * - returned function.length will be the same as the first task function's length
36
+ * @param opts Options for creating a serial task, details in `SerialTaskOptions`
37
+ * @returns a funtcion that executes the tasks in order, returns `TaskReturn<OriginalReturn>`
38
+ *
39
+ * ## About
40
+ * @package SerialTask
41
+ * @author Kasukabe Tsumugi <futami16237@gmail.com>
42
+ * @version 1.0.0 (Last Update: 2025.08.22 17:05:02.346)
43
+ * @license MIT
44
+ * @link https://github.com/baendlorel/serial-task
45
+ * @description Put a list of functions in and get a composed task function. Similar to functional programming's compose (function composition), but with more fine-grained and precise control, and the generated task incurs almost no runtime overhead. Supports both synchronous and asynchronous functions.
46
+ * @copyright Copyright (c) 2025 Kasukabe Tsumugi. All rights reserved.
47
+ */
48
+ declare function createSerialTask<F extends Fn>(opts: SerialTaskOptions<F>): Taskify<F>;
49
+
50
+ export { createSerialTask, createSerialTaskAsync };
51
+
52
+ // # from: src/global.d.ts
53
+ /* eslint-disable @typescript-eslint/no-explicit-any */
54
+ type Fn = (...args: any[]) => any;
55
+
56
+ interface TaskReturn<R = any> {
57
+ /**
58
+ * The result of the last task function
59
+ */
60
+ value: R;
61
+
62
+ /**
63
+ * All results of the tasks
64
+ * - same order as `tasks`
65
+ * - skipped tasks will be empty slot in this array
66
+ * - which means `index in results` is `false`
67
+ */
68
+ results: R[];
69
+
70
+ /**
71
+ * Means `options.tasks.length` is `0`
72
+ */
73
+ trivial: boolean;
74
+
75
+ /**
76
+ * If the task breaks, this will record the index of the task that caused the break.
77
+ * - `-1` means not broken
78
+ * - otherwise, the index of the task that caused the break
79
+ */
80
+ breakAt: number;
81
+
82
+ /**
83
+ * Index of the skipped tasks
84
+ */
85
+ skipped: number[];
86
+ }
87
+
88
+ type TaskifyAsync<F extends Fn> = (...args: Parameters<F>) => Promise<TaskReturn<ReturnType<F>>>;
89
+ type Taskify<F extends Fn> = (...args: Parameters<F>) => TaskReturn<ReturnType<F>>;
90
+
91
+ interface SerialTaskOptions<F extends Fn> {
92
+ /**
93
+ * Name of the generated task function
94
+ * - default is `'kskbTask'`
95
+ */
96
+ name?: string;
97
+
98
+ /**
99
+ * Functions to be executed in order. The one that iterates internally.
100
+ * - if you use `createSerialTaskAsync`, these functions will be called with `await`
101
+ * - **Strongly Recommended**: all task functions must have same input type and output type
102
+ * - creator will directly use this array, so you can modify it dynamically
103
+ * - will be executed from `0` to `length - 1`
104
+ */
105
+ tasks: F[] | Fn[];
106
+
107
+ /**
108
+ * Returns an array of arguments that will be passed to the next task
109
+ * - **MUST return an array of arguments!**
110
+ * - if you use `createSerialTaskAsync`, this function will be called with `await`
111
+ * - when calling the first task(no result before), the `lastReturn` will be set to `undefined`
112
+ * @default
113
+ * (_task: Fn, index: number, _tasks: Fn[], args: unknown[], lastReturn: unknown) => index === 0 ? args : [lastReturn]
114
+ * @param task current task function
115
+ * @param index index of current task
116
+ * @param tasks is `options.tasks`, since `for tasks.length` loop is used here, you can add new tasks dynamically
117
+ * @param args input value of the whole serial task
118
+ * @param lastReturn returned value of the last task function
119
+ * @returns **must return an array of arguments!**
120
+ * @example
121
+ * ```typescript
122
+ * // Internal implementation
123
+ * // first loop, index = 0
124
+ * // The calling order of each function is as follows:
125
+ * const inputValue = [...arguments]; // arguments of the created task function
126
+ * let returnValue = null
127
+ * for(...){
128
+ * const currentInput = resultWrapper(index, ...inputValue, returnValue);
129
+ * const toBreak = breakCondition(index, ...currentInput);
130
+ * if (toBreak) break;
131
+ * const toSkip = skipCondition(index, ...currentInput);
132
+ * if (toSkip) continue;
133
+ * returnValue = tasks[index](...currentInput);
134
+ * }
135
+ * ```
136
+ */
137
+ resultWrapper?: (
138
+ task: F,
139
+ index: number,
140
+ tasks: F[],
141
+ args: Parameters<F>,
142
+ lastReturn: ReturnType<F>
143
+ ) => unknown[];
144
+
145
+ /**
146
+ * Break the loop and return the last result immediately when this function returns `true`
147
+ * - if you use `createSerialTaskAsync`, this function will be called with `await`
148
+ * - when calling the first task(no result before), the `lastReturn` will be `undefined`
149
+ * - default is `() => false`
150
+ * @param task current task function
151
+ * @param index index of current task
152
+ * @param tasks is `options.tasks`, since `for tasks.length` loop is used here, you can add new tasks dynamically
153
+ * @param args input value of the whole serial task
154
+ * @param lastReturn returned value of the last task function
155
+ */
156
+ breakCondition?: (
157
+ task: F,
158
+ index: number,
159
+ tasks: F[],
160
+ args: Parameters<F>,
161
+ lastReturn: ReturnType<F>
162
+ ) => boolean;
163
+
164
+ /**
165
+ * Give `true` to skip this task item
166
+ * - if you use `createSerialTaskAsync`, this function will be called with `await`
167
+ * - when calling the first task(no result before), the `lastReturn` will be `undefined`
168
+ * - default is `() => false`
169
+ * @param task current task function
170
+ * @param index index of current task
171
+ * @param tasks is `options.tasks`, since `for tasks.length` loop is used here, you can add new tasks dynamically
172
+ * @param args input value of the whole serial task
173
+ * @param lastReturn returned value of the last task function
174
+ */
175
+ skipCondition?: (
176
+ task: F,
177
+ index: number,
178
+ tasks: F[],
179
+ args: Parameters<F>,
180
+ lastReturn: ReturnType<F>
181
+ ) => boolean;
182
+ }
183
+
184
+ type StrictSerialTaskOptions<F extends Fn> = Required<SerialTaskOptions<F>>;
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ const t=Reflect.defineProperty,e=Array.isArray,n=Promise.resolve.bind(Promise),r=Promise.reject.bind(Promise),o=t=>("object"==typeof t&&null!==t||"function"==typeof t)&&("then"in t&&"function"==typeof t.then),i=()=>!1,a=i,s=(t,e,n,r,o)=>0===e?r:[o];function u(t){if("object"!=typeof t||null===t)throw new TypeError("SerialTask: 'options' must be an object");const{name:n="kskbTask",tasks:r,breakCondition:o=i,skipCondition:u=a,resultWrapper:l=s}=t;if("string"!=typeof n)throw new TypeError("SerialTask: 'name' must be a string or omitted");if(!e(r)||r.some(t=>"function"!=typeof t))throw new TypeError("SerialTask: 'tasks' must be a function array");if("function"!=typeof o)throw new TypeError("SerialTask: 'breakCondition' must be a function or omitted");if("function"!=typeof u)throw new TypeError("SerialTask: 'skipCondition' must be a function or omitted");if("function"!=typeof l)throw new TypeError("SerialTask: 'resultWrapper' must be a function or omitted");return{name:n,tasks:r,breakCondition:o,skipCondition:u,resultWrapper:l}}function l(t,e,...i){try{const r=t.apply(e,i);return o(r)?r:n(r)}catch(t){return r(t)}}function c(t,e,i){try{const r=t.apply(e,i);return o(r)?r:n(r)}catch(t){return r(t)}}function f(e){const{name:n,tasks:r,breakCondition:o,skipCondition:i,resultWrapper:a}=u(e);if(0===r.length){const e=()=>({value:void 0,results:[],trivial:!0,breakAt:-1,skipped:[]});return t(e,"name",{value:n,configurable:!0}),e}const s=async function(...t){let e;const n=new Array(r.length);let s=-1;const u=[];for(let f=0;f<r.length;f++){const p=r[f],k=await l(a,null,p,f,r,t,e);if(await l(o,null,p,f,r,t,e)){s=f;break}await l(i,null,p,f,r,t,e)?u.push(f):(e=await c(p,null,k),n[f]=e)}return{value:e,results:n,trivial:!1,breakAt:s,skipped:u}};return t(s,"name",{value:n,configurable:!0}),t(s,"length",{value:r[0].length,configurable:!0}),s}function p(e){const{name:n,tasks:r,breakCondition:o,skipCondition:i,resultWrapper:a}=u(e);if(0===r.length){const e=()=>({value:void 0,results:[],trivial:!0,breakAt:-1,skipped:[]});return t(e,"name",{value:n,configurable:!0}),e}const s=function(...t){let e;const n=new Array(r.length);let s=-1;const u=[];for(let l=0;l<r.length;l++){const c=r[l],f=a(c,l,r,t,e);if(o(c,l,r,t,e)){s=l;break}i(c,l,r,t,e)?u.push(l):(e=c.apply(null,f),n[l]=e)}return{value:e,results:n,trivial:!1,breakAt:s,skipped:u}};return t(s,"name",{value:n,configurable:!0}),t(s,"length",{value:r[0].length,configurable:!0}),s}export{p as createSerialTask,f as createSerialTaskAsync};
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "serial-task",
3
+ "version": "1.0.0",
4
+ "author": {
5
+ "name": "Kasukabe Tsumugi",
6
+ "email": "futami16237@gmail.com"
7
+ },
8
+ "purpose": "npm",
9
+ "description": "Put a list of functions in and get a composed task function. Similar to functional programming's compose (function composition), but with more fine-grained and precise control, and the generated task incurs almost no runtime overhead. Supports both synchronous and asynchronous functions.",
10
+ "description_zh": "将一组函数放入并获取一个组合任务函数。类似于函数式编程的函数组合,但具有更细粒度和精确的控制,生成的任务几乎没有运行时判定的额外开销。支持同步和异步函数。",
11
+ "type": "module",
12
+ "module": "./dist/index.mjs",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.mjs",
17
+ "default": "./dist/index.mjs"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "homepage": "https://github.com/baendlorel/serial-task#readme",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/baendlorel/serial-task"
26
+ },
27
+ "keywords": [
28
+ "typescript",
29
+ "javascript"
30
+ ],
31
+ "scripts": {
32
+ "test": "clear & vitest",
33
+ "lint": "oxlint .",
34
+ "cover": "clear & vitest --coverage",
35
+ "build": "node ./scripts/rollup.mjs"
36
+ },
37
+ "license": "MIT",
38
+ "devDependencies": {
39
+ "@babel/plugin-proposal-decorators": "^7.28.0",
40
+ "@babel/preset-env": "^7.28.3",
41
+ "@rollup/plugin-alias": "^5.1.1",
42
+ "@rollup/plugin-babel": "^6.0.4",
43
+ "@rollup/plugin-commonjs": "^28.0.6",
44
+ "@rollup/plugin-node-resolve": "^16.0.1",
45
+ "@rollup/plugin-replace": "^6.0.2",
46
+ "@rollup/plugin-terser": "^0.4.4",
47
+ "@rollup/plugin-typescript": "^12.1.4",
48
+ "@types/node": "^24.3.0",
49
+ "@vitest/coverage-v8": "^3.2.4",
50
+ "oxlint": "^1.12.0",
51
+ "prettier": "^3.6.2",
52
+ "rimraf": "^6.0.1",
53
+ "rollup": "^4.47.1",
54
+ "rollup-plugin-dts": "^6.2.3",
55
+ "rollup-plugin-dts-merger": "^1.2.3",
56
+ "tslib": "^2.8.1",
57
+ "typescript": "^5.9.2",
58
+ "vitest": "^3.2.4"
59
+ }
60
+ }