Hacktoberfest 2026 : les issues que les mainteneurs ont marquées pour octobre, ouvertes et accessibles aux débutants. Parcourir les issues Hacktoberfest

A codegen-free "tolerant store" mode — decouple store schema evolution from native rebuilds

Ouverte
#406 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 4 jours

Personne n'a encore pris cette issue.

Évaluation

Difficulté
5/5
Temps estimé
Plus d'une semaine
Accessibilité débutants
38/100
Type d'issue
Fonctionnalité
Clarté
Plutôt claire
Activité
Calme
Stack technique
cpp, kotlin, react-native, swift, typescript

Piste de recherche

La proposition nomme le store JSI C++ utilisant folly::dynamic, StoreManager, nativeStoreDidChange(key), ainsi que les chemins Swift Codable et Kotlin @Serializable ; commencez par localiser ces points d’entrée. La tâche est terminée lorsqu’un mode tolerant-store documenté et pris en charge, avec le wiring proposé, existe et que le comportement a été validé sur TypeScript, iOS, Android et C++.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

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 module augmentation and use it. No generation step.
  • Swift — a plain Codable struct. JSONDecoder/Codable already ignores unknown keys by default; extra fields in the dynamic state simply aren't decoded, no error.
  • Kotlin — a @Serializable data class decoded with Json { 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:

  1. The native shell writes hostconfig (theme / locale / session / flags) into the C++ folly::dynamic store at startup and on any change.
  2. C++ emits a per-key nativeStoreDidChange(key); StoreManager refreshes only that store and notifies its listeners.
  3. Every federated widget in the shared JS context reads hostconfig from the same StoreManager cache — one source of truth, no per-remote copy.
  4. 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

Langage dominant
TypeScript
Étoiles
552
Forks
53
Merge moyen
3 j 13 h
PR mergées (30 j)
16

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de callstack/react-native-brownfield

Toutes les issues de callstack/react-native-brownfield

Issues similaires

Plus d'issues TypeScript

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.