A codegen-free "tolerant store" mode — decouple store schema evolution from native rebuilds
維護者通常 3 天內回覆
還沒有人認領這個 Issue。
評估
- 難度
- 5/5
- 預估耗時
- 一週以上
- 新手友好度
- 38/100
- Issue 類型
- 功能
- 描述清晰度
- 基本清楚
- 活躍度
- 冷清
- 技術堆疊
- cpp, kotlin, react-native, swift, typescript
研究方向
提案列出了使用 folly::dynamic 的 C++ JSI store、StoreManager、nativeStoreDidChange(key),以及 Swift Codable 和 Kotlin @Serializable 路徑;先定位這些入口點。完成意味著具備一個有文件記錄且受支援的 tolerant-store 模式,採用提議的 wiring,並且其行為已在 TypeScript、iOS、Android 和 C++ 上完成驗證。
由索引模型根據 Issue 內容生成。
描述
Summary
Add an optional, codegen-free "tolerant store" mode alongside the existing generated-types path. Instead of generating exact native mirrors of the TS store type, the native side tolerantly (de)serializes and ignores unknown properties, and the shape is described only in TS.
This lets store-shape changes ship over-the-air (new RN bundle) without forcing a native rebuild + app-store release, while keeping the current strict/generated mode available for teams that want compile-time guarantees.
We run this variant in production today and would like to propose upstreaming it as a documented, supported mode. We're happy to contribute the docs + wiring + an optional validator as PRs.
Motivation
Codegen is great for strictness, but it couples every store-shape change to a native build + app-store release cycle:
add a field → regenerate Swift/Kotlin → recompile the native binary → ship through review
The JS bundle alone can't introduce a new store or field that the native side will accept.
In brownfield apps, the native shell and the RN bundle very often ship on different cadences (native = store review; RN = OTA / CodePush / remote bundle). Forcing a native rebuild for a pure data-shape change breaks that decoupling — which is the whole reason teams adopt OTA in the first place.
Proposed solution: the "tolerant store" model
C++ holds the full state as a dynamic value (folly::dynamic) — it's already the bridge representation. This is the lossless source of truth; it carries every field, including ones a given platform doesn't know about.
Each side decodes only the subset it declares, ignoring the rest:
- TypeScript — describe the shape with the usual
declare moduleaugmentation and use it. No generation step. - Swift — a plain
Codablestruct.JSONDecoder/Codablealready ignores unknown keys by default; extra fields in the dynamic state simply aren't decoded, no error. - Kotlin — a
@Serializabledata class decoded withJson { ignoreUnknownKeys = true }— same tolerance.
Writes are field-scoped merges: a partial setState({ a }) merges into the dynamic state in C++, so fields owned by other consumers are preserved across a write from any side. Unknown-to-this-platform fields survive a native write because they live in the dynamic layer, not in the platform struct.
Net effect: TS is the place you add a field; native keeps working without recompilation as long as it doesn't need to read the new field. The day native wants that field, you add it to the native struct — still no codegen, just one property.
Real-world architecture (our production setup)
This isn't hypothetical. Our brownfield native shell hosts a single RN runtime, and all UI widgets are Re.Pack Module-Federation remotes loaded into that one JS context. Everything — native and every federated widget — reads and writes the same hostconfig store through our store layer (Unistore). Each remote ships on its own cadence, so a shared-config field change cannot be allowed to force a native rebuild.
flowchart TB
subgraph NATIVE["Native host (brownfield shell)"]
iOS["iOS · Swift<br/>Codable struct"]
AND["Android · Kotlin<br/>@Serializable data class"]
end
subgraph CORE["Shared state backbone"]
CPP["C++ JSI store · folly::dynamic<br/>source of truth · dedup · change bus"]
HC["hostconfig store<br/>theme · locale · session · feature flags · env"]
CPP --- HC
end
subgraph RN["Single RN runtime — one JS context"]
SM["StoreManager<br/>one JS cache + listeners"]
subgraph MF["Re.Pack · Module Federation"]
W1["Widget A<br/>(MF remote)"]
W2["Widget B<br/>(MF remote)"]
W3["Widget C<br/>(MF remote)"]
end
SM --- W1
SM --- W2
SM --- W3
end
iOS <-->|hostconfig r/w| CPP
AND <-->|hostconfig r/w| CPP
CPP <-->|"JSI bridge · nativeStoreDidChange(key)"| SM
Data flow for hostconfig:
- The native shell writes
hostconfig(theme / locale / session / flags) into the C++folly::dynamicstore at startup and on any change. - C++ emits a per-key
nativeStoreDidChange(key);StoreManagerrefreshes only that store and notifies its listeners. - Every federated widget in the shared JS context reads
hostconfigfrom the sameStoreManagercache — one source of truth, no per-remote copy. - A widget can write back (e.g. toggling a preference); the partial merge lands in the C++ dynamic store and propagates to native and to the other widgets — bidirectionally.
Why this validates the proposal: hostconfig is consumed by N independently-deployed remotes. With codegen, adding one field means *regenerate native types → rebuild + re-release the native shell → only
- 主要語言
- TypeScript
- 星號
- 553
- 分支
- 58
- 平均合併
- 3 天 18 小時
- 30 天內合併 PR
- 16
環境準備
- 沒有 Dockerfile 或 Docker Compose 檔案
- 沒有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
callstack/react-native-brownfield 的其他 Issue
-
難度 2/5 1-3 小時 新手友好度 85/100
callstack/react-native-brownfield#502 · 1 則留言 ·
維護者通常 3 天內回覆
-
navigation:codegen: optional number and boolean params break the iOS build可能已有人在做 @RaphaelFakhri 於 2 天前認領。 未關閉
難度 3/5 1-2 天 新手友好度 45/100
callstack/react-native-brownfield#518 ·
維護者通常 3 天內回覆
-
Android: initialize cannot be retried after it throws可能已有人在做 @malopezr7 於 8 天前認領。 未關閉
難度 3/5 1-2 天 新手友好度 57/100
callstack/react-native-brownfield#515 ·
維護者通常 3 天內回覆
-
`package:ios --add-spm-package`: allow project-declared extra XCFrameworks as binary targets可能已有人在做 @DanielPotorCK 於 10 天前認領。 未關閉
難度 4/5 3-5 天 新手友好度 55/100
callstack/react-native-brownfield#468 ·
維護者通常 3 天內回覆
-
iOS: emitOnBrownfieldMessage: can crash with std::bad_function_call when native posts before JS requires the TurboModule (cold-start race)可能已有人在做 @tlq5l 於 7 天前認領。 未關閉
難度 4/5 3-5 天 新手友好度 48/100
callstack/react-native-brownfield#420 ·
維護者通常 3 天內回覆
查看 callstack/react-native-brownfield 的全部 Issue
相似的 Issue
-
難度 2/5 1-3 小時 新手友好度 68/100
維護者通常 1 天內回覆
-
documentation easy good first issue help wanted
難度 1/5 1-3 小時 新手友好度 83/100
維護者通常 1 天內回覆
-
Billion-context documentation addendum: nudge is independent of window occupancy. | billion-context 文档补充:nudge 与窗口占用无关可能已有人在做 關聯的 PR 仍在進行中或已合併。 未關閉
難度 1/5 1-3 小時 新手友好度 86/100
ranxianglei/billion-context#5893 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 79/100
microsoft/monaco-editor#5575 ·
維護者通常 1 天內回覆
-
enhancement
難度 2/5 1-3 小時 新手友好度 68/100