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.

runner 的站内导航把 ?api= 从地址栏抹掉:刷新/分享该 URL 会静默退回空的本地打包加载器 #3578

Closed

Description

@yinlianghui

越界发现,记录于 #3537(给 runner 补 api 查询参数文档)期间。未在该 PR 中顺手修改 —— 那是纯文档单,这里要动的是 packages/runner/src/App.tsx 的实现。PR #3537 已把这一行为如实写成注意事项,但它本身更像缺陷而不是设计,故另立单。

事实(origin/main,file:line)

  1. 加载器只在挂载时决定一次 —— packages/runner/src/App.tsx:44-57,useMemo(..., []),依赖数组为空:

    const loader = useMemo(() => {
      const params = new URLSearchParams(window.location.search);
      const apiUrl = params.get('api');
      if (apiUrl) return new NetworkLoader(apiUrl);
      return new LocalBundleLoader();
    }, []);
  2. 站内跳转只 push 路径,不带 query —— App.tsx:73-77:

    const handleNavigate = useCallback((to: string) => {
      window.history.pushState({}, '', to);
      ...

    to 来自侧栏导航项的 item.path(packages/runner/src/LayoutRenderer.tsx:104-105 的 a href={item.path} → LayoutRenderer.tsx:151-157 的 handleNavClick → onNavigate(path)),是裸路径。

后果

用 ?api=https://backend.example.com/api 打开 runner → 正常走 NetworkLoader;点一次侧栏导航后,地址栏变成 /customers(?api= 没了)。当前会话不受影响(loader 实例已 memo 住),但:

  • 刷新(F5) → params.get('api') 为 null → 退回 LocalBundleLoader;而 src/app-data/ 在正常安装里是空的(gitignore),于是所有加载返回 null,页面变成 Page not found / 兜底欢迎页。
  • 复制地址栏分享给同事 → 对方拿到的是不带后端的 URL,复现不了你看到的东西。

失败现场不指认根因:没有任何提示说「你刚才那个 API 基址丢了」,控制台只会打印 📦 Using Local Bundle Loader。

可选方向(不预设结论)

  1. handleNavigate 保留当前 query string(pushState({}, '', to + window.location.search))—— 最小改动,?api= 全程可见、可分享、刷新可复现。
  2. 把 API 基址从 query 提到 sessionStorage/localStorage,query 只作为一次性入口 —— 但会引入「地址栏与实际后端不一致」的新歧义,且与 ADR-0054 C3「可寻址状态放 URL」的口径相反。
  3. 判定为设计如此(query 只是本地调试开关),那就只留 docs(runner): runner 真正的 API 基址配置面是 api 查询参数,但全仓文档零处记载 #3537 的文档注意事项,本单关掉。

按 ADR-0054 C3 的分类,「当前指向哪个后端」是典型的可寻址状态(用户会分享、刷新后期待还在),方向 1 与之最一致;但这是维护者判断,故不预设。


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 7, 2026
  2. yinlianghui commented on Aug 7, 2026

    @yinlianghui
    ContributorAuthor

    认领(PM 分诊晋级)— 会话 session_01GTRjn8xBqp75dk7kFupVRt,分支 claude/issue-3578-preserve-api-query。

    契约裁定(issue 留给"维护者判断"的三向,按既有 ADR 定,不上交):方向 1 —— handleNavigate 保留当前 query string。依据:ADR-0054 C3 已裁"可寻址状态放 URL","当前指向哪个后端"是典型可寻址状态(分享、刷新都期待保持);方向 2 与该 ADR 口径相反(地址栏与实际后端可以不一致);方向 3(设计如此)被"失败现场不指认根因"的静默降级否决 —— 这正是本仓最忌的静默失败形状。维护者可在 PR 上否决。

    实现注意:保留的应是整个 query string 还是仅 api 参数,dev 按"最小惊讶"实测定(若 runner 还有其它 query 语义,逐个点名);?api= 存在与否两种状态下的导航都要测。#3537/PR #3581 刚写进文档的注意事项在修复落地后要同步改掉(文档不许描述已修复的缺陷行为)—— runner.mdx 那段 caveat 在本单文件面内。

    文件面:packages/runner/src/App.tsx(handleNavigate)+ 测试 + content/docs/utilities/runner.mdx(仅那段 caveat 的同步)。⚠️ PR #3581 刚合并改过 runner.mdx —— 从最新 origin/main 切分支。越界即停。


    Generated by Claude Code

  3. yinlianghui commented on Aug 7, 2026

    @yinlianghui
    ContributorAuthor

    验收:ACCEPT,open question 裁 A(README 编辑保留) — PR #3594。

    核过 GitHub 实物(6 文件 = 围栏 4 + 两处已声明偏离)。要点:(1) whole-query 选择有测量支撑 —— ?__debug* 家族在渲染树内被 core/react 直读,api-only 修法会静默丢第二个活语义;(2) 逆验证 3红/1绿如实预告(第四条在旧码上平凡通过是防过度应用的守卫,不硬造红);(3) 一次"pre-existing"误判被自己抓回(未跟踪文件让回退实验失真),真因(tsconfig.test.json 缺 vite/client)修法对齐四个兄弟包既有形状 —— 这类自我纠错正是报告该有的样子;(4) README 越栏裁 A:围栏点名 mdx 是"文档不许描述已修复缺陷"规则的实例而非边界,同一事实第二份拷贝必须同 PR 清;(5) #3595(nav path 带 query 的路由分叉,休眠)已核存在入池。⚠️ 时序记录:#3576 与本 PR 在 README 相邻 hunk,等本 PR 落 main 再派。18/18 CI 绿。PR 转 ready + auto-merge。


    Generated by Claude Code

  4. added a commit that references this issue on Aug 10, 2026
    04fb8b8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions