Repository navigation
Commit 9f7e408
fix(mobile): the native bridge platform is an open set, and a newer bridge protocol reads as no bridge (#29475)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbGJyQF5yrTTG9tojdS1NGJy)_
Fixes #29330
Clause-②: yes (widening)
Metadata-protocol: none
## 维护者速读
- **改了什么**:原生壳桥的 `platform` 类型从 `ios | android`
放开为开放集合(桌面壳可报其它值);新增桥协议版本门:壳报的 `version` 大于 1 或不是数字时,Console
当作没有壳、走浏览器路径;写明 `ready` 的含义;README 示例去掉按壳切换文案的分支。
- **为什么改**:桌面壳即将上线且报 `desktop`,旧的封闭类型会让消费方写出漏掉它的穷举分支;`version`
从未被读,将来协议破坏性升级时旧 Console 会误调新协议;壳将依赖 `ready`
揭开加载遮罩(mobile#162),需要明确它在鉴权加载态就会发出。
- **风险与回滚**:运行时只多一道版本判断,现有壳都报版本 1,行为不变;类型放宽只可能让对 `platform` 做穷举 switch
的代码编译不过,全仓只有 Console 一处把它原样写进属性。回滚即 revert 本 PR。
- **先看**:`ui/packages/mobile/src/nativeHost.ts`(`getNativeHost` 的版本门与
`platform` 类型)→
`ui/apps/console/src/__tests__/App.nativeShellOpenSet-29330.test.tsx` →
`ui/apps/console/src/__tests__/main.nativeBridgeVersion-29330.test.tsx`。
- **两分钟验证**:在 `ui/` 下跑 `pnpm exec vitest run
apps/console/src/__tests__/App.nativeShellOpenSet-29330.test.tsx`,预期 4
个用例全绿(桌面壳、鉴权加载态时的 ready、版本 2 不进壳模式及其对照)。
- **需要你做的**:无,席位落地。
## What changes
- **`ObjectStackNative.platform` is an open set.** The type widens from
`'ios' | 'android'` to `'ios' | 'android' | (string & {})`, so the two
known values still autocomplete and the desktop shell's value
type-checks. The runtime already treated it as open: `getNativeHost`
never checked it, the Console writes it verbatim into
`data-os-native-shell`, and the call wrappers gate on `has()` only. The
README and the pending `.changeset/29039-mobile-native-shell-mode.md`
now say the attribute carries the shell's platform string, an open set.
- **Bridge protocol version gate.** The bridge type says `version`
"bumps only on a breaking change". `nativeHost.ts` gains
`NATIVE_BRIDGE_VERSION = 1` (module export used by the tests; not added
to the package entry, which this PR leaves untouched). `getNativeHost()`
answers `null` for a host whose `version` is not a number (NaN included)
or is greater than 1. Every reader goes through it, so such a host gets
the browser behaviour: no shell mode, no `ready`, no `routeChanged`, no
navigate listener, the PWA manifest kept, and (since #29326 merged) the
service worker treated as in a browser.
- **What `ready` means, decided: document it, don't move it.** `ready`
means "the Console has painted its own UI", which may be a loading
fallback such as `AuthGuard`'s spinner on a cold start to a protected
route. The shell lifts its loading cover on `ready`, so the cover hands
over to the Console's own spinner; waiting for `AuthGuard` would hold
the native cover through a network round-trip. Written on the
`ObjectStackNative.ready` member, the `postNativeReady` and
`useNativeHostRouting` docblocks, and the README's bridge section. The
timing is unchanged and now pinned.
- **README nit.** The `SaveButton` example drops `host ? 'Save' : 'Save
changes'` and keeps one label, following the "viewport and capability,
not host" rule.
## Tests
- `ui/apps/console/src/__tests__/App.nativeShellOpenSet-29330.test.tsx`
(new, the real `App`, `ProtectedRoute` and `AuthGuard` under the real
`BrowserRouter`):
- a `platform: 'desktop'` bridge announcing `haptic` and `openExternal`
only: shell mode on, `ready` posted once,
`data-os-native-shell="desktop"`, the two announced calls reach the
shell and `getPushToken`, `navigate` and `resetServer` resolve
`unavailable` and are never called;
- a cold start to `/` with the session read still open: `ready` arrives
while `AuthGuard`'s loading fallback is on screen and before the route
renders;
- a `version: 2` bridge: no root attribute, no `ready`, no route post,
calls `unavailable`; control: the same bridge at `version: 1` is the
shell.
-
`ui/apps/console/src/__tests__/main.nativeBridgeVersion-29330.test.tsx`
(new, the real `main.tsx` with its boot calls and its manifest icon step
stubbed): a `version: 2` bridge starts the manifest step and the
generated manifest replaces the static one; controls: `version: 1` never
starts the step and leaves no manifest, no bridge behaves like `version:
2`.
- `ui/packages/mobile/src/__tests__/nativeHost.test.tsx` (extended): a
`desktop` bridge is a bridge; `NATIVE_BRIDGE_VERSION` is 1; a newer,
string, NaN or missing version is `null` and its calls are `unavailable`
and uncalled; versions 1 and 0 are the bridge; `useNativeHostRouting`
posts nothing and installs no listener for a version 2 bridge.
## Acceptance notes
- **Reverse checks.** Version gate ablated (the `typeof version` line
replaced): 7 cases red across the three files (main version 2, App
version 2, four mobile detection cases, mobile routing case), restored
byte-identical to HEAD. Old closed union restored on `platform`: `tsc
--noEmit` in `ui/apps/console` reds the desktop fixture with TS2322
"Type 'string' is not assignable to type '"ios" | "android"'", restored
byte-identical.
- **Widening, not narrowing, for readers.** `git grep` of the bridge
symbols across `ui/`, `packages/` and `apps/` finds only
`@object-ui/mobile` and the Console; the one reader of `platform` is
`NativeShellRouting`, which writes it into an attribute. No exhaustive
switch exists to break.
- **Expected red: `check-empty-changeset`.** This PR edits the pending
`.changeset/29039-mobile-native-shell-mode.md`, wording only:
`data-os-native-shell="ios|android"` becomes the shell's platform
string, an open set. That is the gate's deliberate-correction class: the
old sentence is false after this PR, so it is corrected here rather than
restored, by the card's ruling. The gate stays red by design; it is not
one of the required contexts.
- **Surface addition.** The manifest half of the version-gate pin lives
where the decision lives, `main.tsx`, so it has its own test file beside
the App-level one. No source file outside the card's surface changed;
`App.tsx` and `index.ts` are untouched.
- **Merged with `main` after #29326** (service worker), which also edits
`ui/packages/mobile/README.md` and `main.tsx`; the merge was clean and
the version gate now also covers its worker decision through
`getNativeHost()`. A second merge brought #29405, which moved the
manifest into an async step after first paint; the manifest test waits
for that step and pins whether it starts.
## Verification
All readings at head `28cb3792` (`main` merged at `a6441636`; the 13
`main` commits since touch none of this PR's 8 files).
- Touched suites, from `ui/`: `pnpm exec vitest run` of the two new
console files, `App.nativeShellMode-29039.test.tsx`, `packages/mobile/`
and `scripts/__tests__/check-vi-mock-inherit.test.ts`: 13 files, 220
tests passed.
- `pnpm --filter @object-ui/mobile run type-check` and `pnpm --filter
@object-ui/console run type-check` (both cover the test files): exit 0.
`eslint` on the five touched source and test files: exit 0.
- Derived gates: `node scripts/pm/dispatch-gates.mjs --commands` lists
34; all 34 run and reconciled with `--ran` (0 NOT-MEASURED). 33 exit 0,
including `pnpm --dir ui exec vitest run scripts/` (169 files, 4921
tests) and `check-sdui-manifest`; `check-empty-changeset` exits 1, the
expected deliberate-correction red above.
- Changed lines: 524 (+515 / -9) in 8 files, under the 3000-line
threshold.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jSpg4SQPs6XFLsrBhv)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 8118be4 commit 9f7e408
8 files changed
Lines changed: 515 additions & 9 deletions
File tree
- .changeset
- ui
- apps/console/src/__tests__
- packages/mobile
- src
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
0 commit comments