@@ -380,12 +380,62 @@ export class Task<
380380 * Resets input data to defaults
381381 */
382382 public resetInputData ( ) : void {
383- // Use deep clone to avoid state leakage
384- try {
385- this . runInputData = structuredClone ( this . defaults ) as Record < string , any > ;
386- } catch ( err ) {
387- this . runInputData = JSON . parse ( JSON . stringify ( this . defaults ) ) as Record < string , any > ;
383+ this . runInputData = this . smartClone ( this . defaults ) as Record < string , any > ;
384+ }
385+
386+ /**
387+ * Smart clone that deep-clones plain objects and arrays while preserving
388+ * class instances (objects with non-Object prototype) by reference.
389+ *
390+ * This is necessary because:
391+ * - structuredClone cannot clone class instances (methods are lost)
392+ * - JSON.parse/stringify loses methods and fails on circular references
393+ * - Class instances like repositories should be passed by reference
394+ *
395+ * This breaks the idea of everything being json serializable, but it allows
396+ * more efficient use cases. Do be careful with this though! Use sparingly.
397+ *
398+ * @param obj The object to clone
399+ * @returns A cloned object with class instances preserved by reference
400+ */
401+ private smartClone ( obj : any ) : any {
402+ if ( obj === null || obj === undefined ) {
403+ return obj ;
404+ }
405+
406+ // Preserve TypedArrays (Float32Array, Int8Array, etc.) by reference
407+ // These are often large and cloning them is expensive
408+ if ( ArrayBuffer . isView ( obj ) ) {
409+ return obj ;
388410 }
411+
412+ // Preserve class instances (objects with non-Object/non-Array prototype)
413+ // This includes repository instances, custom classes, etc.
414+ if ( typeof obj === "object" && ! Array . isArray ( obj ) ) {
415+ const proto = Object . getPrototypeOf ( obj ) ;
416+ if ( proto !== Object . prototype && proto !== null ) {
417+ return obj ; // Pass by reference
418+ }
419+ }
420+
421+ // Deep clone arrays, preserving class instances within
422+ if ( Array . isArray ( obj ) ) {
423+ return obj . map ( ( item ) => this . smartClone ( item ) ) ;
424+ }
425+
426+ // Deep clone plain objects
427+ if ( typeof obj === "object" ) {
428+ const result : Record < string , any > = { } ;
429+ for ( const key in obj ) {
430+ if ( Object . prototype . hasOwnProperty . call ( obj , key ) ) {
431+ result [ key ] = this . smartClone ( obj [ key ] ) ;
432+ }
433+ }
434+ return result ;
435+ }
436+
437+ // Primitives (string, number, boolean, symbol, bigint) are returned as-is
438+ return obj ;
389439 }
390440
391441 /**
0 commit comments