From 94da14cd81f4e4c75d1f2e184fc79a389b8da0c2 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Thu, 4 Jun 2026 13:19:55 -0700 Subject: [PATCH] fix: lazy-load esbuild via createRequire (jsdom-safe engine import) + lint cleanup of dead helpers Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/core/src/task-fields.ts | 7 ------- .../app/components/TaskFieldsSection.tsx | 8 -------- packages/engine/src/code-node-runner.ts | 16 ++++++++++++++-- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/core/src/task-fields.ts b/packages/core/src/task-fields.ts index ce4c0cd019..93fc8a0a9b 100644 --- a/packages/core/src/task-fields.ts +++ b/packages/core/src/task-fields.ts @@ -24,7 +24,6 @@ import type { WorkflowFieldDefinition, - WorkflowFieldType, } from "./workflow-ir-types.js"; // --------------------------------------------------------------------------- @@ -187,7 +186,6 @@ function validateValue( return reject("type-mismatch", `field '${field.id}' (url) expects a string, got ${typeof value}`); } try { - // eslint-disable-next-line no-new new URL(value); } catch { return reject("type-mismatch", `field '${field.id}' value '${value}' is not a valid URL`); @@ -299,11 +297,6 @@ export function applyFieldDefaults( // Reconciliation on workflow edit / switch // --------------------------------------------------------------------------- -/** Two field types are "enum-kind" siblings (enum / multi-enum). */ -function isEnumKind(type: WorkflowFieldType): boolean { - return type === "enum" || type === "multi-enum"; -} - /** * A stored value for `field` is type-compatible with a new field definition iff * the new value re-validates cleanly. For enum-kind fields, compatibility also diff --git a/packages/dashboard/app/components/TaskFieldsSection.tsx b/packages/dashboard/app/components/TaskFieldsSection.tsx index 4343d3becb..67d7cfb76e 100644 --- a/packages/dashboard/app/components/TaskFieldsSection.tsx +++ b/packages/dashboard/app/components/TaskFieldsSection.tsx @@ -60,14 +60,6 @@ export interface TaskFieldsSectionProps { readOnly?: boolean; } -function optionLabel(field: WorkflowFieldDefinition, value: string): string { - return field.options?.find((o) => o.value === value)?.label ?? value; -} - -function optionColor(field: WorkflowFieldDefinition, value: string): string | undefined { - return field.options?.find((o) => o.value === value)?.color; -} - /** Resolve the effective widget for a field, applying the per-type default. */ function resolveWidget(field: WorkflowFieldDefinition): NonNullable["widget"] { const explicit = field.render?.widget; diff --git a/packages/engine/src/code-node-runner.ts b/packages/engine/src/code-node-runner.ts index 44652ccab7..760fb7a667 100644 --- a/packages/engine/src/code-node-runner.ts +++ b/packages/engine/src/code-node-runner.ts @@ -39,11 +39,23 @@ */ import { execFile } from "node:child_process"; +import { createRequire } from "node:module"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { transformSync } from "esbuild"; +// esbuild is loaded lazily at first compile: a top-level import would run its +// environment invariant check (TextEncoder) at module-load time in any process +// that merely imports @fusion/engine — including jsdom test environments where +// that invariant fails. Lazy loading confines esbuild to actual code-node use. +let cachedTransformSync: typeof import("esbuild").transformSync | undefined; +function getTransformSync(): typeof import("esbuild").transformSync { + if (!cachedTransformSync) { + const req = createRequire(import.meta.url); + cachedTransformSync = (req("esbuild") as typeof import("esbuild")).transformSync; + } + return cachedTransformSync; +} import type { CustomFieldRejection, TaskDetail, WorkflowIrNode } from "@fusion/core"; import type { WorkflowNodeResult } from "./workflow-graph-executor.js"; @@ -148,7 +160,7 @@ export async function compileCodeNodeSource(source: string): Promise { // `transformSync` runs a short-lived per-call child that exits cleanly, // avoiding esbuild's long-lived service process (which the test harness's // subprocess guard would otherwise flag as a lingering child). - const out = transformSync(source, { + const out = getTransformSync()(source, { loader: "ts", format: "esm", target: "node18",