From ebff7db11e0eb69141173d2841bac7bb7f143604 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Thu, 30 Jul 2026 10:40:43 +0800 Subject: [PATCH] fix(schema-form): extract the clone-spine walk and drop the unused ui-primitives dependency --- packages/client/schema-form/package.json | 1 - packages/client/schema-form/src/model.ts | 59 ++++++++++++----------- packages/client/schema-form/tsconfig.json | 3 -- pnpm-lock.yaml | 3 -- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/packages/client/schema-form/package.json b/packages/client/schema-form/package.json index 29adb51133..af03b9c8b0 100644 --- a/packages/client/schema-form/package.json +++ b/packages/client/schema-form/package.json @@ -20,7 +20,6 @@ }, "license": "BSD-3-Clause", "dependencies": { - "@deepseek-ai/dsh-client-ui-primitives": "workspace:^", "react": "^18.2.0", "schemastery": "^3.18.0" }, diff --git a/packages/client/schema-form/src/model.ts b/packages/client/schema-form/src/model.ts index 17038f7c84..4c695d0b67 100644 --- a/packages/client/schema-form/src/model.ts +++ b/packages/client/schema-form/src/model.ts @@ -117,7 +117,13 @@ export function getPath(value: unknown, path: readonly string[]): unknown { return current } -/** Whether a draft explicitly carries the path (its presence marks a user override). */ +/** + * Whether a draft explicitly carries the path (its presence marks a user + * override, independent of the value stored there). + * @param value - root value (draft or fallback layer). + * @param path - key path from the root; array indexes as strings. + * @returns whether the path's final key exists on its parent. + */ export function hasPath(value: unknown, path: readonly string[]): boolean { if (path.length === 0) return value !== undefined const parent = getPath(value, path.slice(0, -1)) @@ -134,15 +140,12 @@ function cloneContainer(container: unknown, key: string): Record, path: readonly string[], value: unknown): Record { - if (path.length === 0) throw new Error('schema-form: setPath needs a non-empty path') +/** Clone the container spine down to the leaf's parent, materializing missing intermediates. */ +function cloneSpine(root: Record, path: readonly string[]): { + result: Record + parent: Record | unknown[] + leaf: string +} { const result = { ...root } let target: Record | unknown[] = result for (let i = 0; i < path.length - 1; i++) { @@ -155,9 +158,21 @@ export function setPath(root: Record, path: readonly string[], else (target)[key] = child target = child } - const leaf = path[path.length - 1] as string - if (Array.isArray(target)) target[Number(leaf)] = value - else (target)[leaf] = value + return { result, parent: target, leaf: path[path.length - 1] as string } +} + +/** + * Immutably set a nested value, materializing missing intermediate containers. + * @param root - draft root (never mutated). + * @param path - non-empty key path. + * @param value - value to store at the path. + * @returns the new draft root. + */ +export function setPath(root: Record, path: readonly string[], value: unknown): Record { + if (path.length === 0) throw new Error('schema-form: setPath needs a non-empty path') + const { result, parent, leaf } = cloneSpine(root, path) + if (Array.isArray(parent)) parent[Number(leaf)] = value + else parent[leaf] = value return result } @@ -172,20 +187,8 @@ export function setPath(root: Record, path: readonly string[], export function deletePath(root: Record, path: readonly string[]): Record { if (path.length === 0) throw new Error('schema-form: deletePath needs a non-empty path') if (!hasPath(root, path)) return root - const result = { ...root } - let target: Record | unknown[] = result - for (let i = 0; i < path.length - 1; i++) { - const key = path[i] as string - const child = cloneContainer( - Array.isArray(target) ? target[Number(key)] : (target)[key], - path[i + 1] as string, - ) - if (Array.isArray(target)) target[Number(key)] = child - else (target)[key] = child - target = child - } - const leaf = path[path.length - 1] as string - if (Array.isArray(target)) target.splice(Number(leaf), 1) - else Reflect.deleteProperty(target, leaf) + const { result, parent, leaf } = cloneSpine(root, path) + if (Array.isArray(parent)) parent.splice(Number(leaf), 1) + else Reflect.deleteProperty(parent, leaf) return result } diff --git a/packages/client/schema-form/tsconfig.json b/packages/client/schema-form/tsconfig.json index 44a9376434..a47bdb4ecb 100644 --- a/packages/client/schema-form/tsconfig.json +++ b/packages/client/schema-form/tsconfig.json @@ -8,9 +8,6 @@ "src" ], "references": [ - { - "path": "../ui-primitives" - }, { "path": "../../../vendor/schemastery" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8755e918b1..4250410f63 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -956,9 +956,6 @@ importers: packages/client/schema-form: dependencies: - '@deepseek-ai/dsh-client-ui-primitives': - specifier: workspace:^ - version: link:../ui-primitives react: specifier: ^18.2.0 version: 18.3.1