From f64e843c3dcbc4e9866a540633b103a0a09075cb Mon Sep 17 00:00:00 2001 From: lda Date: Sat, 29 Aug 2026 21:23:50 +0700 Subject: [PATCH] refactor: consolidate authoring binding copies --- src/wf_core/analysis/context_scopes.py | 3 --- .../workspace/authoring/useDraftAuthoring.ts | 22 ++-------------- .../domain/draft-authoring-client.test.ts | 15 ++++++++++- .../domain/draft-authoring-client.ts | 25 +++++++++++-------- 4 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/wf_core/analysis/context_scopes.py b/src/wf_core/analysis/context_scopes.py index c75f9f0e..84d114e8 100644 --- a/src/wf_core/analysis/context_scopes.py +++ b/src/wf_core/analysis/context_scopes.py @@ -134,7 +134,6 @@ def _analyze(workflow: Workflow) -> _ContextAnalysis: fields_by_node[node_id] = _available_fields( workflow, foreach_nodes, - node_id, scopes, scopes_by_node, ) @@ -144,11 +143,9 @@ def _analyze(workflow: Workflow) -> _ContextAnalysis: def _available_fields( workflow: Workflow, foreach_nodes: Mapping[str, ForeachNode], - node_id: str, scopes: set[FrameScope], scopes_by_node: Mapping[str, set[FrameScope]], ) -> tuple[ContextFieldAvailability, ...]: - del node_id fields_by_name: dict[str, ContextFieldContract] = {} scopes_by_field: dict[str, set[FrameScope]] = {} for scope in sorted(scopes, key=lambda value: value or ""): diff --git a/web/apps/console/src/workspace/authoring/useDraftAuthoring.ts b/web/apps/console/src/workspace/authoring/useDraftAuthoring.ts index 2d866b79..2045bedf 100644 --- a/web/apps/console/src/workspace/authoring/useDraftAuthoring.ts +++ b/web/apps/console/src/workspace/authoring/useDraftAuthoring.ts @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useConsoleWorkspace } from "../context.js"; import { createDraftAuthoringClient, + copyInputBinding, copyStepInputBinding, type DraftAuthoringClient, } from "../domain/draft-authoring-client.js"; @@ -193,26 +194,7 @@ const copyOutputBindings = ( const copyWorkflowOutputBindings = ( bindings: ReadonlyArray, -): ReadonlyArray => bindings.map((binding) => ( - "path" in binding - ? { - path: - typeof binding.path === "string" - ? binding.path - : { root: binding.path.root, parts: [...binding.path.parts] }, - target: - typeof binding.target === "string" - ? binding.target - : { root: binding.target.root, parts: [...binding.target.parts] }, - } - : { - value: copyJson(binding.value), - target: - typeof binding.target === "string" - ? binding.target - : { root: binding.target.root, parts: [...binding.target.parts] }, - } -)); +): ReadonlyArray => bindings.map(copyInputBinding); const copyContractPatch = (patch: WorkflowContractPatch): WorkflowContractPatch => ({ ...(patch.inputSchema !== undefined diff --git a/web/apps/console/src/workspace/domain/draft-authoring-client.test.ts b/web/apps/console/src/workspace/domain/draft-authoring-client.test.ts index 507f6ac3..16706685 100644 --- a/web/apps/console/src/workspace/domain/draft-authoring-client.test.ts +++ b/web/apps/console/src/workspace/domain/draft-authoring-client.test.ts @@ -18,7 +18,7 @@ import { type SetWorkflowStartInput, type UpdateCapabilityStepInput, } from "./draft-workspace-models.js"; -import { createDraftAuthoringClient } from "./draft-authoring-client.js"; +import { copyInputBinding, createDraftAuthoringClient } from "./draft-authoring-client.js"; import type { ConsoleWriteExecutor } from "./write-executor.js"; const canonicalWorkspace = { @@ -65,6 +65,19 @@ const createExecutor = () => { }; describe("DraftAuthoringClient", () => { + it("copies workflow-compatible input bindings without sharing nested paths", () => { + const binding = { + path: { root: "state" as const, parts: ["report"] }, + target: { root: "local" as const, parts: ["text"] }, + } satisfies InputBinding; + + const copied = copyInputBinding(binding); + expect(copied).not.toBe(binding); + if (!("path" in copied)) throw new Error("expected a path binding"); + expect(copied.path).not.toBe(binding.path); + expect(copied.target).not.toBe(binding.target); + }); + it("lowers all six authoring operations and decodes canonical workspaces", async () => { const { executor: writeExecutor, run } = createExecutor(); const client = createDraftAuthoringClient(writeExecutor); diff --git a/web/apps/console/src/workspace/domain/draft-authoring-client.ts b/web/apps/console/src/workspace/domain/draft-authoring-client.ts index 0933429b..bc227661 100644 --- a/web/apps/console/src/workspace/domain/draft-authoring-client.ts +++ b/web/apps/console/src/workspace/domain/draft-authoring-client.ts @@ -5,6 +5,7 @@ import { type CreateEmptyDraftInput, type CreateFromCapabilityInput, type DraftWorkspace, + type InputBinding, type JsonValue, type InputExpression, type InputPath, @@ -112,27 +113,31 @@ export const copyInputExpression = ( } }; -export const copyStepInputBinding = ( - binding: StepInputBinding, -): StepInputBinding => { +export const copyInputBinding = (binding: InputBinding): InputBinding => { if ("path" in binding) { return { path: copyInputPath(binding.path), target: copyLocalInputPath(binding.target), }; } - if ("value" in binding) { - return { - target: copyLocalInputPath(binding.target), - value: copyJsonValue(binding.value), - }; - } return { target: copyLocalInputPath(binding.target), - expression: copyInputExpression(binding.expression), + value: copyJsonValue(binding.value), }; }; +export const copyStepInputBinding = ( + binding: StepInputBinding, +): StepInputBinding => { + if ("expression" in binding) { + return { + target: copyLocalInputPath(binding.target), + expression: copyInputExpression(binding.expression), + }; + } + return copyInputBinding(binding); +}; + const copyOutputBinding = (binding: OutputBinding): OutputBinding => ({ source: typeof binding.source === "string"