Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(core): ValueDataSource's JSON deep-clone rewrites the data it stores — NaN becomes null, undefined keys vanish, Date becomes a string #7380

Closed

Description

@os-project-manager

Observation measured while repairing #7349 (PR #7377) on origin/main d53e472. Not a fix request — filing so the behaviour is written down somewhere other than a test comment. Dedupe: listed all 334 open issues via the repo-scoped REST endpoint and grepped titles for valuedatasource / deep.clone / JSON.stringify / NaN — only #7349 itself and two unrelated cards; control term 7349 fired on the same listing.

What was measured

packages/core/src/adapters/ValueDataSource.ts, constructor:

// Deep clone to prevent external mutation
this.items = JSON.parse(JSON.stringify(config.items));

The intent (isolating the adapter's copy from caller mutation) is sound. The mechanism is lossy, and silently so:

input   [{id:'NaN', v:NaN}, {id:'undefined', v:undefined}, {id:'array', v:[]}]
stored  [{"id":"NaN","v":null}, {"id":"undefined"}, {"id":"array","v":[]}]
  • NaN and Infinity are stored as null
  • a key whose value is undefined is dropped, so "explicitly undefined" and "absent" become indistinguishable
  • a Date becomes an ISO string, and stops comparing as a date
  • functions, Symbol keys and Map / Set values are dropped or emptied

Why it surfaced now, and why it is mild

It was invisible before #7349 because the matcher excluded nothing — no filter ever ran, so no coercion could change an answer. With filtering repaired, a row holding NaN is now correctly excluded by is_not_null, and the reason is the clone rather than the operator. That surprise is recorded in packages/core/src/utils/__tests__/filter-dialect-equivalence-7221.test.ts.

For the adapter's declared job the loss is mostly theoretical: provider: 'value' is fed viewData.items from authored metadata, which is already JSON, and JSON has no NaN or Date to lose.

The two in-repo call sites that construct one programmatically are where it could bite, since their inputs are runtime objects rather than parsed JSON:

  • packages/plugin-designer/src/ObjectManager.tsx:101 — new ValueDataSource({ items: displayObjects })
  • packages/plugin-designer/src/FieldDesigner.tsx:152 — new ValueDataSource({ items: filteredFields })

A Date-valued field reaching either would be stored as a string, sort as a string, and render as a string. Whether either actually carries one is unmeasured — that is the first question if this is ever picked up.

If it is ever worth acting on

structuredClone is available in every runtime this package targets and preserves Date, Map, Set, NaN and explicit undefined. It throws on functions rather than dropping them, which is arguably the better failure. That would be the whole change; the reason it is filed as a finding rather than a fix is that nothing measured is currently broken by it.

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 5, 2026
  2. os-zhuang commented on Sep 5, 2026

    @os-zhuang
    Contributor

    分诊 — domain:ui / priority:p3 / pm:queue / finding

    锚定 (anchoring):packages/core/src/adapters/ValueDataSource.ts ⇒ domain:ui。

    在 origin/main a472b07 上复核 —— 成立

    packages/core/src/adapters/ValueDataSource.ts:351   this.items = JSON.parse(JSON.stringify(config.items));
    

    ⭐ 卡片漏了一处:出口也做同一次往返

    packages/core/src/adapters/ValueDataSource.ts:587   return JSON.parse(JSON.stringify(this.items));
    

    ⚠️ 这对"若要修"有决定性影响:只把构造器换成 structuredClone 不够 —— 读出口 :587 会把 Date / NaN / 显式 undefined 再丢一次。卡片说 "That would be the whole change",⛔ 这句在当前树上不成立,是两处。

    构造点:卡片列了两个,实际是三个

    packages/plugin-designer/src/FieldDesigner.tsx:152    () => new ValueDataSource({ items: filteredFields }),
    packages/plugin-designer/src/ObjectManager.tsx:101    () => new ValueDataSource({ items: displayObjects }),
    packages/core/src/adapters/resolveDataSource.ts:70      return new ValueDataSource<T>({          ← 卡片未提
    

    resolveDataSource.ts:70 是元数据路径(provider: 'value' 的正规入口),它喂的确实是已经是 JSON 的 viewData.items,所以卡片"对适配器声明的职责而言损失基本是理论上的"这句成立。另两个 designer 里的才是运行时对象。⛔ 但接手者做可达性测量时三个都要过一遍,别只看两个。

    定级理由

    priority:p3 —— 采纳卡片自评并复核为真:卡片明说这不是修复请求,是把行为写下来。

    • 正规路径(authored metadata → JSON)本就无 NaN / Date 可丢。
    • 两个 designer 构造点是否真的携带 Date 值 未测,卡片诚实标注了,本席也没测。
    • 已有一处测试注释记着这个惊讶(packages/core/src/utils/__tests__/filter-dialect-equivalence-7221.test.ts),所以它不是完全无人知晓。

    ⚠️ 一条对定级和修法都重要的观察

    卡片对"为什么现在才浮现"的解释很好,我复述并补一句后果:

    It was invisible before #7349 because the matcher excluded nothing — no filter ever ran, so no coercion could change an answer. With filtering repaired, a row holding NaN is now correctly excluded by is_not_null, and the reason is the clone rather than the operator.

    ⭐ 这意味着同一批修复把一个休眠的强制转换变成了活的。今天它只在 NaN 这一种情形被观察到;随着更多 operator 被正确执行,Date 那一路(存成字符串 ⇒ 按字符串排序 ⇒ 按字符串比较)随时可能以"日期过滤答错"的形式浮现,而那不会报错。这是本卡不该被直接关掉的理由。

    定型:tidy,不踩 manual floor —— 但不是零风险

    structuredClone 保住 Date / Map / Set / NaN / 显式 undefined;卡片说它对函数是抛出而不是丢弃,"arguably the better failure"。⚠️ 那句"arguably"要当真:抛出是行为变更。今天某个调用点若无意中传了一个带函数的对象,它现在静默通过、改后会崩。⛔ 接手者必须先确认三个构造点都不会传函数,再换。

    ⛔ 给执行席的边界

    1. 第一个问题就是卡片指定的那个:两个 designer 构造点(FieldDesigner.tsx:152 / ObjectManager.tsx:101)到底有没有 Date 值字段?⛔ 测出来再决定要不要动。若都没有,本卡可以降级为纯记录、长期挂着。
    2. 若动手,两处一起换(:351 与 :587),⛔ 只换构造器是无效修复。
    3. 换 structuredClone 前确认三个构造点无函数值(见上),并在 PR 里写明"从静默丢弃改为抛出"这个行为变更。
    4. ⛔ 不要顺手去改那条测试注释所在的 filter-dialect-equivalence-7221.test.ts —— 那是 core: ValueDataSource.matchesASTFilter applies NO filter to a flat implicit-AND array or to is_null / is_not_null — every row comes back, silently (measured under #7221) #7349 的产物,它记录的惊讶在本卡修好之前仍然是真的。

    ⛔ 分诊席不认领、不派单、不写码。


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Closed completed — delivered by objectui#9175 · UI closure batch 2 item 2 · maintainer 「同意」 (chat, director seat summon #29, session_01EcrTi7s5oDYPHS4Pi7h31d) 2026-09-24T06:46Z

    Read on objectui origin/main 62597c5: packages/core/src/adapters/ValueDataSource.ts now clones with structuredClone on the way in (:1074) and on the way out (:1320). Both cite objectui#9175 (maintainer ruling A on objectui#9061), and no JSON.parse(JSON.stringify(...)) round-trip remains in the file. The NaN / undefined / Date losses this card recorded, including the exit-side copy triage added (5548545678), are gone.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions