11// Libs
22import { objType , superValidator , validateTotal } from './utils/essentials.mjs' ;
33
4+ /**
5+ * @typedef {Object } forPromiseIteration
6+ * @property {Record<string, any>|any[]|number } data - The main data source. Can be an array-like object, number (as count), or object for keys.
7+ * @property {string } [type] - Optional type of iteration. Currently supports "while" for while-like looping.
8+ * @property {* } [while] - While validator
9+ * @property {Function } [checker] - Used only with type: "while" to determine loop continuation.
10+ */
11+
12+ /**
13+ * Extra control object.
14+ *
15+ * @typedef {Object } forPromiseExtra
16+ * @property {boolean } complete - Whether the extra item has completed processing.
17+ * @property {boolean } forceBreak - Whether the extra item has triggered a forced break.
18+ * @property {number } count - Number of processed items.
19+ * @property {number|null } total - Total expected items, or null if not set.
20+ * @property {Array<any> } items - List of items processed.
21+ * @property {string } [type] - Optional type of processing (e.g., 'while').
22+ * @property {boolean } [error] - Is error.
23+ */
24+
25+ /**
26+ * @typedef {{
27+ * error: boolean;
28+ * forceBreak: boolean;
29+ * type?: string;
30+ * extra?: Array<forPromiseExtra>;
31+ * count: number;
32+ * total: number|null;
33+ * items: Array<any>
34+ * }} ForPromiseStatus
35+ */
36+
437/**
538 * Runs an asynchronous iterative operation with advanced control over flow, breaks, and nested (extra) iterations.
639 * The function accepts a data object and a callback, and returns a Promise that resolves with iteration results.
740 *
8- * @function
9- * @param {Object } obj - The data object used to control the iteration process. It must follow a specific structure.
10- * @param {Object|number } obj.data - The main data source. Can be an array-like object, number (as count), or object for keys.
11- * @param {string } [obj.type] - Optional type of iteration. Currently supports `"while"` for while-like looping.
12- * @param {Function } [obj.checker] - Used only with `type: "while"` to determine loop continuation.
41+ * @param {forPromiseIteration } obj - The data object used to control the iteration process. It must follow a specific structure.
1342 * @param {Function } callback - A function that will be called on each iteration step.
14- * The callback receives the following parameters:
15- *
16- * @callback callback
17- * @param {* } currentItem - The current item or index being iterated.
18- * @param {Function } result - Function to call when iteration step finishes. Accepts an optional forceBreak configuration.
19- * @param {Function } error - Function to call if an error is encountered.
20- * @param {Function } extra - Function to call to register additional (nested) iterations.
21- *
22- * @returns {Promise<Object> } Resolves with an object containing the result of the iteration process.
23- *
24- * The returned object structure is:
25- * @typedef {Object } IterationResult
26- * @property {boolean } error - Whether an error occurred.
27- * @property {boolean } forceBreak - Whether the iteration was forcefully broken.
28- * @property {number } count - How many steps were executed.
29- * @property {number } total - Total expected steps.
30- * @property {Array } items - The collected results from each callback.
31- * @property {Array } [extra] - If extras were used, contains an array of extra iteration results with the same structure.
32- *
33- * The `result()` function (inside the callback) accepts an optional argument:
34- * @param {boolean|Object } [forceBreak=false] - If true, breaks the iteration early.
35- * If an object is passed, it can contain:
36- * @property {boolean } [break=false] - Whether to break the loop.
37- * @property {boolean } [dontSendResult=false] - Prevents auto-resolve after completion.
38- * @property {boolean } [forceResult=false] - Forces result resolution regardless of loop status.
3943 *
40- * The `extra()` function (inside the callback) is used to spawn additional loops within the current loop.
41- * It must be passed a valid object like the main one, and returns:
42- * @returns {Object } An object with a `run(callback)` method to run the extra loop.
43- *
44- * @throws {Error } Throws if invalid input types are provided or during execution.
45- *
46- * @example
47- * const obj = { data: [1, 2, 3] };
48- *
49- * forPromise(obj, (item, result, error, extra) => {
50- * console.log(item);
51- * result(); // move to next
52- * }).then(res => console.log(res));
53- *
54- * @example
55- * const input = {
56- * data: [1, 2, 3],
57- * };
58- *
59- * forPromise(input, (item, result, error, extraFn) => {
60- * if (item === 2) return result({ break: true });
61- * result();
62- * }).then(console.log).catch(console.error);
44+ * @returns {Promise<ForPromiseStatus> }
6345 */
64- export default function forPromise ( obj , callback ) {
65- return new Promise ( function ( resolve , reject ) {
46+ export default async function forPromise ( obj , callback ) {
47+ return new Promise ( ( resolve , reject ) => {
6648 try {
6749 if ( typeof obj !== 'object' || obj === null ) throw new Error ( 'Invalid object provided.' ) ;
6850 if ( typeof callback !== 'function' ) throw new Error ( 'Callback must be a function.' ) ;
@@ -75,7 +57,7 @@ export default function forPromise(obj, callback) {
7557
7658 // Validator
7759 if ( objValidated . confirmed ) {
78- // Prepare Count
60+ /** @type { ForPromiseStatus } */
7961 let items = {
8062 error : false ,
8163 forceBreak : false ,
@@ -85,13 +67,21 @@ export default function forPromise(obj, callback) {
8567 } ;
8668
8769 // Error Result
70+ /** @param {Error } err */
8871 const error_result = function ( err ) {
8972 // Send Error Reject
9073 items . error = true ;
9174 reject ( err ) ;
9275 } ;
9376
94- // Prepare Result
77+ /**
78+ * Prepare Result
79+ *
80+ * @param {boolean } isExtra
81+ * @param {number } extraIndex
82+ * @param {* } item
83+ * @param {boolean|{ break: boolean; dontSendResult: boolean; forceResult: boolean;} } [forceBreak=false]
84+ */
9585 const result = function ( isExtra , extraIndex , item , forceBreak = false ) {
9686 // Prepare Edit
9787 let item_to_edit = null ;
@@ -103,8 +93,22 @@ export default function forPromise(obj, callback) {
10393 item_to_edit = extra . list [ extraIndex ] ;
10494 }
10595
96+ if ( typeof item_to_edit . total !== 'number' )
97+ return reject ( new Error ( 'Invalid "item_to_edit.total" value: null or undefined.' ) ) ;
98+
10699 // Force Break
107- const forceBreakResult = { isObject : objType ( forceBreak , 'object' ) } ;
100+ /**
101+ * @typedef {Object } ForceBreakResult
102+ * @property {boolean } isObject
103+ * @property {boolean } [allowed]
104+ * @property {boolean } [dontSendResult]
105+ * @property {boolean } [forceResult]
106+ */
107+
108+ /** @type {ForceBreakResult } */
109+ const forceBreakResult = {
110+ isObject : /** @type {boolean } */ ( objType ( forceBreak , 'object' ) ) ,
111+ } ;
108112
109113 // Is Boolean
110114 if ( ! forceBreakResult . isObject ) {
@@ -118,7 +122,7 @@ export default function forPromise(obj, callback) {
118122 }
119123
120124 // Object
121- else {
125+ else if ( typeof forceBreak === 'object' ) {
122126 if ( ! item_to_edit . forceBreak )
123127 forceBreakResult . allowed = typeof forceBreak . break === 'boolean' && forceBreak . break ;
124128 else forceBreakResult . allowed = false ;
@@ -200,23 +204,32 @@ export default function forPromise(obj, callback) {
200204 }
201205 } ;
202206
203- // Run For
204- const runFor = function ( callback , isExtra = false , index = null , new_extra = null ) {
205- // Prepare the Item
207+ /**
208+ * @param {Function } callback
209+ * @param {boolean } [isExtra=false]
210+ * @param {number } [index=-1]
211+ * @param {{data: any, type?: string, checker?: Function}|null } [new_extra=null]
212+ * @returns {void }
213+ */
214+ const runFor = function ( callback , isExtra = false , index = - 1 , new_extra = null ) {
206215 let the_item = null ;
207216
208217 // Normal
209218 if ( ! isExtra ) the_item = obj ;
210- else the_item = new_extra ;
219+ else if ( typeof new_extra === 'object' ) the_item = new_extra ;
211220
212- // Run Script
221+ /**
222+ * @param {number|string|null } [item=null]
223+ * @returns {boolean }
224+ */
213225 const runFor_script = function ( item = null ) {
214226 // No Error
215227 if ( ! items . error && ! items . forceBreak ) {
216228 // Try
217229 try {
218230 // Result Function
219- const result_data = ( forceBreak ) => result ( isExtra , index , item , forceBreak ) ;
231+ const result_data = /** @param {boolean } forceBreak */ ( forceBreak ) =>
232+ result ( isExtra , index , item , forceBreak ) ;
220233
221234 // Exist Item
222235 if ( item !== null ) callback ( item , result_data , error_result , extra . extra_function ) ;
@@ -237,6 +250,8 @@ export default function forPromise(obj, callback) {
237250 else return false ;
238251 } ;
239252
253+ if ( the_item === null ) throw new Error ( 'Invalid "the_item" value: null or undefined.' ) ;
254+
240255 // Start the For
241256 if ( typeof the_item . data !== 'number' ) {
242257 // For Object
@@ -258,8 +273,11 @@ export default function forPromise(obj, callback) {
258273 else {
259274 // Start a While
260275 if ( the_item . type === 'while' ) {
261- // Prepare
276+ /** @returns { object|void } */
262277 const custom_do = function ( ) {
278+ if ( typeof the_item . checker !== 'function' )
279+ throw new Error ( 'Invalid "checker" function in "the_item".' ) ;
280+
263281 // Validate
264282 if ( the_item . checker ( ) ) {
265283 // Prepare Edit
@@ -268,15 +286,22 @@ export default function forPromise(obj, callback) {
268286 // Not Extra
269287 if ( ! isExtra ) {
270288 item_to_edit = items ;
271- } else {
272- item_to_edit = extra . list [ index ] ;
273- }
289+ } else item_to_edit = extra . list [ index ] ;
290+
291+ if ( typeof item_to_edit . total !== 'number' )
292+ throw new Error ( 'Invalid "item_to_edit.total" value: null or undefined.' ) ;
274293
275294 // Add Total
276295 item_to_edit . total ++ ;
277296
278297 // Callback and Continue
279298 callback (
299+ /**
300+ * Increments the item count and returns the result handler.
301+ *
302+ * @param {boolean } forceBreak - Whether to forcefully break the loop.
303+ * @returns {* } The result of the handler execution.
304+ */
280305 function ( forceBreak ) {
281306 item_to_edit . count ++ ;
282307 return result ( isExtra , index , null , forceBreak ) ;
@@ -325,15 +350,27 @@ export default function forPromise(obj, callback) {
325350 // Type
326351 if ( objValidated . type ) items . type = objValidated . type ;
327352
328- // Prepare Extra
353+ /**
354+ * Represents the extra handling structure.
355+ *
356+ * @typedef {Object } Extra
357+ * @property {boolean } enabled - Whether extra processing is enabled.
358+ * @property {Array<forPromiseExtra> } list - List of extra items.
359+ * @property {function(forPromiseIteration): {run: function(Function): void}|null } extra_function - Function to add a new extra item.
360+ */
361+
362+ /** @type {Extra } */
329363 const extra = {
330364 // Enabled
331365 enabled : false ,
332366
333367 // Extra List
334368 list : [ ] ,
335369
336- // Functions
370+ /**
371+ * @param {forPromiseIteration } new_extra
372+ * @returns {{run: function(Function): void}|null }
373+ */
337374 extra_function : function ( new_extra ) {
338375 // Validate Obj
339376 const objValidated = superValidator ( new_extra ) ;
0 commit comments