Skip to content

Commit f096238

Browse files
committed
Document why appVersion stays explicit, and prove an unreadable one fails open
1 parent d84a845 commit f096238

4 files changed

Lines changed: 50 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
- `appVersion` stays required here, while the Swift and Flutter SDKs now detect
6+
it. React Native exposes nothing that reads the host app's version — reaching
7+
the bundle needs a native module, and this package having none is worth more
8+
than saving a caller one argument. Documented rather than papered over.
59
- Removed the kill switch, following its removal from the protocol. Evaluation
610
order is now maintenance → force → soft → none. Old payloads that still carry
711
a `kill` block parse fine; the block is ignored.

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ No native modules, no pod install, no config plugin. It is JavaScript.
2525
## Quick start
2626

2727
```tsx
28-
import { RipstopProvider } from '@ripstop/react-native';
2928
import AsyncStorage from '@react-native-async-storage/async-storage';
30-
import { asyncStorageAdapter } from '@ripstop/react-native';
29+
import DeviceInfo from 'react-native-device-info';
30+
import { RipstopProvider, asyncStorageAdapter } from '@ripstop/react-native';
3131

3232
export default function App() {
3333
return (
@@ -45,6 +45,26 @@ export default function App() {
4545
That's the whole integration. The provider renders your app until a decision
4646
says otherwise, then shows the right wall.
4747

48+
## Why you still pass `appVersion`
49+
50+
The other Ripstop SDKs read the installed version themselves — Swift from
51+
`CFBundleShortVersionString`, Flutter from `package_info_plus`. React Native
52+
cannot. Nothing in the framework exposes the host app's version: `Platform.OS`
53+
and `Platform.Version` describe the operating system, and
54+
`Platform.constants.reactNativeVersion` describes React Native. Reading the
55+
bundle means a native module, and this package has none — see the installation
56+
note above, which is a promise worth more than saving you one argument.
57+
58+
So pass it, from whichever of these you already have:
59+
60+
```tsx
61+
import DeviceInfo from 'react-native-device-info'; // DeviceInfo.getVersion()
62+
import Constants from 'expo-constants'; // Constants.expoConfig?.version
63+
```
64+
65+
Pass the version only — `1.4.0`, not `1.4.0 (312)`. Build numbers are not part
66+
of a semantic version, and the rules are compared as semantic versions.
67+
4868
## Or draw your own
4969

5070
```tsx

src/client.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,18 @@ const DEFAULT_ENDPOINT = 'https://cfg.ripstop.dev/v1/config';
1818

1919
export interface RipstopOptions {
2020
apiKey: string;
21-
/** The build the page is running. Rules are evaluated against this. */
21+
/**
22+
* The build this app is running. Rules are evaluated against it.
23+
*
24+
* Required, unlike the Swift and Flutter SDKs, which read it themselves.
25+
* React Native exposes no way to: `Platform.Version` is the OS and
26+
* `Platform.constants.reactNativeVersion` is the framework, and reaching the
27+
* bundle means a native module. This package has none, and that is worth
28+
* more than saving a caller one argument.
29+
*
30+
* The version only — `1.4.0`, not `1.4.0 (312)`. Build numbers are not part
31+
* of a semantic version, and these are compared as semantic versions.
32+
*/
2233
appVersion: string;
2334
platform?: Platform;
2435
locale?: string;

test/client.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,18 @@ describe('the client', () => {
120120
expect(gate.source).toBe('none');
121121
});
122122

123+
it('has no opinion about a version it cannot read', async () => {
124+
// `appVersion` is required here because React Native will not tell us the
125+
// installed version, which puts the value in the caller's hands — so the
126+
// common mistakes have to fail open rather than wall somebody. A display
127+
// string with the build number in it is the one people reach for.
128+
const { impl } = server(config());
129+
for (const appVersion of ['', '1.4.0 (312)', 'v3.0.0']) {
130+
const gate = await boot(impl, { appVersion });
131+
expect((await gate.check()).type, appVersion).toBe('none');
132+
}
133+
});
134+
123135
it('refuses an unknown key id', async () => {
124136
const { impl } = server(config());
125137
const gate = await boot(impl, { signingKeys: { other: publicKeyB64 } });

0 commit comments

Comments
 (0)