Skip to content

Commit f456f5a

Browse files
committed
[refactor] Implement smartClone method for improved input data handling for tests
- Replaced structuredClone and JSON methods with a new smartClone function that deep-clones plain objects and arrays while preserving class instances by reference.
1 parent c04c7f8 commit f456f5a

File tree

1 file changed

+55
-5
lines changed
  • packages/task-graph/src/task

1 file changed

+55
-5
lines changed

packages/task-graph/src/task/Task.ts

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)