Commit 2ab02bb
authored
Fix React Native runtime visibility packaging (#4683)
Fixes React Native/Expo app lifecycle handling for ShapeStream by
replacing brittle runtime `require('react-native')` probing with a
Metro/Expo `react-native` package export. React Native builds now wire
`AppState` automatically, while `runtimeVisibility` remains available as
an explicit escape hatch for custom bundlers/runtimes.
## Root cause
The previous React Native visibility detection depended on runtime
access to `require('react-native')` via `globalThis.require` or
`Function('return require')`. In Metro/Hermes builds, `require` is
available at module scope but not necessarily on `globalThis`, and the
function fallback can return `undefined`. When that happened,
`runtimeVisibility` stayed unset and the AppState listener was never
installed.
## Approach
- Remove the brittle runtime React Native detection from `ShapeStream`.
- Add a React Native-specific entrypoint (`src/react-native.ts`) that
statically imports `AppState` from `react-native` and registers the
default runtime visibility adapter.
- Add `browser` and `react-native` conditional package exports, plus an
explicit `./react-native` subpath and legacy top-level `react-native`
field, so Metro/Expo resolve the correct entrypoint for web, native, and
explicit React Native imports:
```ts
import { ShapeStream } from '@electric-sql/client'
```
- Keep `runtimeVisibility` as the explicit override/fallback for custom
runtimes or Metro configs that do not resolve the `react-native`
condition.
- Mark only the React Native entrypoints as side-effectful in
`package.json` so the AppState registration is not tree-shaken while
preserving tree-shaking for the rest of the package.
- Expose both ESM and CJS targets and matching `.d.ts`/`.d.cts`
declaration files under the RN conditional export to match the package's
existing dual-format publishing.
## Key invariants
- Browser/Expo web/Node/default imports do not statically import
`react-native`.
- React Native/Metro imports run the RN entrypoint and install the
AppState-backed adapter.
- User-provided `runtimeVisibility` still wins over the default adapter.
- Hidden/background state pauses streams and aborts in-flight requests;
visible/active state resumes with non-live catch-up.
- Non-RN package entrypoints remain side-effect-free for tree-shaking.
## Non-goals
- Do not add `react-native` as a runtime dependency of
`@electric-sql/client`.
- Do not rely on additional global `require` probing or Metro internals.
- Do not replace the existing `runtimeVisibility` API; it remains the
escape hatch.
- Do not introduce an RN-specific `ShapeStream` wrapper in this PR.
## Trade-offs
The RN entrypoint uses top-level registration through a small shared
factory. This keeps the main `ShapeStream` implementation simple and
avoids duplicating the class for React Native, but it means the RN
entrypoint must be marked as side-effectful. The `sideEffects` allowlist
is limited to the RN entry files so other package entrypoints remain
tree-shakable.
## Verification
```bash
pnpm install
pnpm --dir packages/typescript-client run typecheck
pnpm --dir packages/typescript-client run stylecheck
cd packages/typescript-client && pnpm build
```
I also ran the targeted wake-detection unit test with a temporary Vitest
config that skips the integration global setup; all 15 tests in
`test/wake-detection.test.ts` passed. The normal package test command
attempted to run global integration setup and timed out waiting for
Electric to be active in this environment.
## Files changed
- `.changeset/fresh-ravens-foreground.md` — patch changeset for
`@electric-sql/client`.
- `packages/typescript-client/package.json` — adds browser/RN
conditional exports, explicit `./react-native` subpath, top-level RN
field, CJS RN target, matching declaration targets, pack-file exclusion
for the local shim, and sideEffects allowlist for RN entrypoints.
- `packages/typescript-client/tsup.config.ts` — builds the RN entrypoint
and externalizes `react-native`.
- `packages/typescript-client/src/client.ts` — removes runtime RN
probing and reads the optional default runtime visibility factory.
- `packages/typescript-client/src/react-native.ts` — RN entrypoint that
wires AppState lifecycle handling and re-exports the normal client API.
- `packages/typescript-client/src/runtime-visibility.ts` — shared
default runtime visibility factory registration.
- `packages/typescript-client/src/react-native-shim.d.ts` — minimal
local typing for the RN entrypoint without adding an RN dependency.
- `packages/typescript-client/test/wake-detection.test.ts` — updates the
RN test to exercise the RN entrypoint instead of global require
detection.
- `packages/typescript-client/SPEC.md` — updates the pause-lock notes to
describe RN handling through package exports.
- `packages/typescript-client/README.md`,
`website/docs/sync/api/clients/typescript.md`,
`website/docs/sync/integrations/expo.md` — document automatic Metro/Expo
handling and explicit `runtimeVisibility` fallback.1 parent 08db805 commit 2ab02bb
12 files changed
Lines changed: 201 additions & 96 deletions
File tree
- .changeset
- packages/typescript-client
- src
- test
- website/docs/sync
- api/clients
- integrations
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
104 | 108 | | |
105 | 109 | | |
106 | 110 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
445 | 445 | | |
446 | 446 | | |
447 | 447 | | |
448 | | - | |
| 448 | + | |
449 | 449 | | |
450 | 450 | | |
451 | 451 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
43 | 65 | | |
44 | 66 | | |
45 | 67 | | |
| |||
48 | 70 | | |
49 | 71 | | |
50 | 72 | | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
51 | 84 | | |
52 | 85 | | |
53 | 86 | | |
| |||
58 | 91 | | |
59 | 92 | | |
60 | 93 | | |
61 | | - | |
| 94 | + | |
| 95 | + | |
62 | 96 | | |
63 | 97 | | |
64 | 98 | | |
65 | 99 | | |
66 | 100 | | |
| 101 | + | |
67 | 102 | | |
68 | 103 | | |
69 | 104 | | |
| |||
83 | 118 | | |
84 | 119 | | |
85 | 120 | | |
86 | | - | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
87 | 126 | | |
88 | 127 | | |
89 | 128 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
88 | 88 | | |
89 | 89 | | |
90 | 90 | | |
| 91 | + | |
91 | 92 | | |
92 | 93 | | |
93 | 94 | | |
| |||
290 | 291 | | |
291 | 292 | | |
292 | 293 | | |
293 | | - | |
294 | | - | |
295 | | - | |
296 | | - | |
297 | | - | |
298 | | - | |
299 | | - | |
300 | | - | |
301 | | - | |
302 | | - | |
303 | | - | |
304 | | - | |
305 | | - | |
306 | | - | |
307 | | - | |
308 | | - | |
309 | | - | |
310 | | - | |
311 | | - | |
312 | | - | |
313 | | - | |
314 | | - | |
315 | | - | |
316 | | - | |
317 | | - | |
318 | | - | |
| 294 | + | |
319 | 295 | | |
320 | 296 | | |
321 | | - | |
322 | | - | |
323 | | - | |
324 | | - | |
325 | | - | |
326 | | - | |
327 | | - | |
328 | | - | |
329 | | - | |
330 | | - | |
331 | | - | |
332 | | - | |
| 297 | + | |
333 | 298 | | |
334 | 299 | | |
335 | 300 | | |
| |||
2020 | 1985 | | |
2021 | 1986 | | |
2022 | 1987 | | |
2023 | | - | |
2024 | | - | |
| 1988 | + | |
2025 | 1989 | | |
2026 | 1990 | | |
2027 | 1991 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
255 | 255 | | |
256 | 256 | | |
257 | 257 | | |
258 | | - | |
| 258 | + | |
259 | 259 | | |
260 | 260 | | |
261 | 261 | | |
262 | 262 | | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
270 | | - | |
271 | | - | |
272 | | - | |
273 | | - | |
274 | | - | |
275 | | - | |
276 | | - | |
277 | | - | |
278 | | - | |
279 | | - | |
280 | | - | |
281 | | - | |
282 | | - | |
283 | | - | |
284 | | - | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
285 | 269 | | |
286 | | - | |
287 | | - | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
288 | 275 | | |
289 | 276 | | |
290 | 277 | | |
| |||
322 | 309 | | |
323 | 310 | | |
324 | 311 | | |
325 | | - | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
326 | 316 | | |
327 | 317 | | |
328 | 318 | | |
| |||
350 | 340 | | |
351 | 341 | | |
352 | 342 | | |
353 | | - | |
354 | | - | |
355 | | - | |
356 | | - | |
357 | | - | |
| 343 | + | |
| 344 | + | |
358 | 345 | | |
359 | 346 | | |
360 | 347 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| 13 | + | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
| |||
0 commit comments