@omniretail/omniflags-react-native 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -0
- package/dist/async-storage-cache.d.ts +15 -0
- package/dist/async-storage-cache.js +36 -0
- package/dist/async-storage-cache.js.map +1 -0
- package/dist/context.d.ts +7 -0
- package/dist/context.js +10 -0
- package/dist/context.js.map +1 -0
- package/dist/hooks.d.ts +9 -0
- package/dist/hooks.js +69 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +9 -0
- package/dist/provider.js +38 -0
- package/dist/provider.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @omniretail/omniflags-react-native
|
|
2
|
+
|
|
3
|
+
React Native SDK for OmniFlags.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @omniretail/omniflags-react-native @react-native-async-storage/async-storage
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Follow the [AsyncStorage setup guide](https://react-native-async-storage.github.io/async-storage/docs/install) to link the native module. If you're using Expo:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx expo install @react-native-async-storage/async-storage
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { OmniFlagsProvider, useFlag } from "@omniretail/omniflags-react-native";
|
|
21
|
+
|
|
22
|
+
function App() {
|
|
23
|
+
return (
|
|
24
|
+
<OmniFlagsProvider sdkKey="your-sdk-key">
|
|
25
|
+
<MyApp />
|
|
26
|
+
</OmniFlagsProvider>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function MyApp() {
|
|
31
|
+
const enabled = useFlag("store.new-checkout");
|
|
32
|
+
return enabled ? <NewCheckout /> : <LegacyCheckout />;
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Provider
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
<OmniFlagsProvider sdkKey="your-sdk-key">
|
|
40
|
+
{children}
|
|
41
|
+
</OmniFlagsProvider>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Prop | Type | Description |
|
|
45
|
+
|------|------|-------------|
|
|
46
|
+
| `sdkKey` | `string` | **Required.** SDK key from the OmniFlags dashboard. |
|
|
47
|
+
|
|
48
|
+
Flags are persisted to AsyncStorage and refreshed when the app returns to the foreground.
|
|
49
|
+
|
|
50
|
+
## Hooks
|
|
51
|
+
|
|
52
|
+
### `useFlag`
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
const enabled = useFlag("store.new-checkout");
|
|
56
|
+
const enabled = useFlag("store.new-checkout", { customerId: "cust-123" }); // with context
|
|
57
|
+
const enabled = useFlag("store.new-checkout", undefined, false); // custom default
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `useFlagValue`
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
const theme = useFlagValue<string>("store.theme", "light");
|
|
64
|
+
const limit = useFlagValue<number>("store.item-limit", 10);
|
|
65
|
+
const theme = useFlagValue<string>("store.theme", "light", { businessId: "biz-456" }); // with context
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `useFlagVariant`
|
|
69
|
+
|
|
70
|
+
Returns the full evaluation result — value, variant, reason, and which rule matched.
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
const result = useFlagVariant("store.new-checkout");
|
|
74
|
+
// result.value, result.variant, result.reason, result.ruleId, result.errorCode
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `useFlagLoading`
|
|
78
|
+
|
|
79
|
+
Returns the current loading state of the flag client.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
const { isFetching, isLoading, origin, error } = useFlagLoading();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
| Field | Type | Description |
|
|
86
|
+
|-------|------|-------------|
|
|
87
|
+
| `isFetching` | `boolean` | A network request is in flight. |
|
|
88
|
+
| `isLoading` | `boolean` | No flags have been loaded yet (first fetch pending). |
|
|
89
|
+
| `origin` | `FlagOrigin` | Where the current flags came from: `"NONE"`, `"CACHE"`, or `"SERVER"`. |
|
|
90
|
+
| `error` | `Error \| null` | The last fetch error, or `null`. |
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
function FlagGate({ children }: { children: React.ReactNode }) {
|
|
94
|
+
const { isLoading } = useFlagLoading();
|
|
95
|
+
if (isLoading) return <ActivityIndicator />;
|
|
96
|
+
return <>{children}</>;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `useOmniFlags`
|
|
101
|
+
|
|
102
|
+
Low-level access to the underlying client and ready state.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
const { client, ready } = useOmniFlags();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Context
|
|
109
|
+
|
|
110
|
+
Context is passed per evaluation call — customer, business, branch, country, etc. There is no persistent context state.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
function CheckoutButton({ customerId, businessBranchId }: Props) {
|
|
114
|
+
const ctx = { customerId, businessBranchId };
|
|
115
|
+
const enabled = useFlag("store.new-checkout", ctx);
|
|
116
|
+
return enabled ? <NewCheckoutButton /> : <LegacyCheckoutButton />;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Flag keys
|
|
121
|
+
|
|
122
|
+
Keys are namespaced by project: `{projectKey}.{flagKey}`. Find the full flag key in the OmniFlags dashboard on the flag detail page.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CacheAdapter } from "@omniretail/omniflags-core";
|
|
2
|
+
import type AsyncStorage from "@react-native-async-storage/async-storage";
|
|
3
|
+
export interface AsyncStorageLike {
|
|
4
|
+
getItem(key: string): Promise<string | null>;
|
|
5
|
+
setItem(key: string, value: string): Promise<void>;
|
|
6
|
+
removeItem(key: string): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export type ReactNativeAsyncStorage = typeof AsyncStorage;
|
|
9
|
+
export declare class AsyncStorageCache implements CacheAdapter {
|
|
10
|
+
private storage;
|
|
11
|
+
constructor(storage: AsyncStorageLike);
|
|
12
|
+
get(key: string): Promise<string | null>;
|
|
13
|
+
set(key: string, value: string): Promise<void>;
|
|
14
|
+
remove(key: string): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AsyncStorage-backed cache for React Native.
|
|
3
|
+
* Uses @react-native-async-storage/async-storage under the hood.
|
|
4
|
+
*/
|
|
5
|
+
const PREFIX = "omniflags:snapshot:";
|
|
6
|
+
export class AsyncStorageCache {
|
|
7
|
+
storage;
|
|
8
|
+
constructor(storage) {
|
|
9
|
+
this.storage = storage;
|
|
10
|
+
}
|
|
11
|
+
async get(key) {
|
|
12
|
+
try {
|
|
13
|
+
return await this.storage.getItem(PREFIX + key);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async set(key, value) {
|
|
20
|
+
try {
|
|
21
|
+
await this.storage.setItem(PREFIX + key, value);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
/* non-fatal — storage may be full */
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async remove(key) {
|
|
28
|
+
try {
|
|
29
|
+
await this.storage.removeItem(PREFIX + key);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
/* ignore */
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=async-storage-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-storage-cache.js","sourceRoot":"","sources":["../src/async-storage-cache.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,MAAM,GAAG,qBAAqB,CAAC;AAUrC,MAAM,OAAO,iBAAiB;IACpB,OAAO,CAAmB;IAElC,YAAY,OAAyB;QACnC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa;QAClC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;QACvC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,YAAY;QACd,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { OmniFlagsClient } from "@omniretail/omniflags-core";
|
|
2
|
+
export interface OmniFlagsContextValue {
|
|
3
|
+
client: OmniFlagsClient;
|
|
4
|
+
ready: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const OmniFlagsContext: import("react").Context<OmniFlagsContextValue | null>;
|
|
7
|
+
export declare function useOmniFlagsContext(): OmniFlagsContextValue;
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
export const OmniFlagsContext = createContext(null);
|
|
3
|
+
export function useOmniFlagsContext() {
|
|
4
|
+
const ctx = useContext(OmniFlagsContext);
|
|
5
|
+
if (!ctx) {
|
|
6
|
+
throw new Error("useOmniFlags* hooks must be used inside <OmniFlagsProvider>");
|
|
7
|
+
}
|
|
8
|
+
return ctx;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAQlD,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAA+B,IAAI,CAAC,CAAC;AAElF,MAAM,UAAU,mBAAmB;IACjC,MAAM,GAAG,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EvaluationContext, EvaluationResult, LoadingState } from "@omniretail/omniflags-core";
|
|
2
|
+
export declare function useFlagLoading(): LoadingState;
|
|
3
|
+
export declare function useOmniFlags(): {
|
|
4
|
+
client: import("@omniretail/omniflags-core").OmniFlagsClient;
|
|
5
|
+
ready: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function useFlag(flagKey: string, context?: EvaluationContext, defaultValue?: boolean): boolean;
|
|
8
|
+
export declare function useFlagValue<T = unknown>(flagKey: string, defaultValue: T, context?: EvaluationContext): T;
|
|
9
|
+
export declare function useFlagVariant(flagKey: string, context?: EvaluationContext): EvaluationResult;
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from "react";
|
|
2
|
+
import { useOmniFlagsContext } from "./context.js";
|
|
3
|
+
export function useFlagLoading() {
|
|
4
|
+
const { client } = useOmniFlagsContext();
|
|
5
|
+
const [state, setState] = useState(() => client.loadingState);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const unsub = client.on("PROVIDER_LOADING_STATE_CHANGED", () => {
|
|
8
|
+
setState(client.loadingState);
|
|
9
|
+
});
|
|
10
|
+
return unsub;
|
|
11
|
+
}, [client]);
|
|
12
|
+
return state;
|
|
13
|
+
}
|
|
14
|
+
export function useOmniFlags() {
|
|
15
|
+
const { client, ready } = useOmniFlagsContext();
|
|
16
|
+
return { client, ready };
|
|
17
|
+
}
|
|
18
|
+
export function useFlag(flagKey, context, defaultValue = false) {
|
|
19
|
+
const { client } = useOmniFlagsContext();
|
|
20
|
+
const contextRef = useRef(context);
|
|
21
|
+
contextRef.current = context;
|
|
22
|
+
const [value, setValue] = useState(() => client.isEnabled(flagKey, context, defaultValue));
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const update = () => {
|
|
25
|
+
const next = client.isEnabled(flagKey, contextRef.current, defaultValue);
|
|
26
|
+
setValue((prev) => (prev === next ? prev : next));
|
|
27
|
+
};
|
|
28
|
+
update();
|
|
29
|
+
const unsubReady = client.on("PROVIDER_READY", update);
|
|
30
|
+
const unsubChange = client.on("PROVIDER_CONFIGURATION_CHANGED", update);
|
|
31
|
+
return () => { unsubReady(); unsubChange(); };
|
|
32
|
+
}, [client, flagKey, defaultValue]);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
export function useFlagValue(flagKey, defaultValue, context) {
|
|
36
|
+
const { client } = useOmniFlagsContext();
|
|
37
|
+
const contextRef = useRef(context);
|
|
38
|
+
contextRef.current = context;
|
|
39
|
+
const [value, setValue] = useState(() => client.getValue(flagKey, context, defaultValue));
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const update = () => {
|
|
42
|
+
const next = client.getValue(flagKey, contextRef.current, defaultValue);
|
|
43
|
+
setValue((prev) => (Object.is(prev, next) ? prev : next));
|
|
44
|
+
};
|
|
45
|
+
update();
|
|
46
|
+
const unsubReady = client.on("PROVIDER_READY", update);
|
|
47
|
+
const unsubChange = client.on("PROVIDER_CONFIGURATION_CHANGED", update);
|
|
48
|
+
return () => { unsubReady(); unsubChange(); };
|
|
49
|
+
}, [client, flagKey, defaultValue]);
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
export function useFlagVariant(flagKey, context) {
|
|
53
|
+
const { client } = useOmniFlagsContext();
|
|
54
|
+
const contextRef = useRef(context);
|
|
55
|
+
contextRef.current = context;
|
|
56
|
+
const [result, setResult] = useState(() => client.getVariant(flagKey, context));
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const update = () => {
|
|
59
|
+
const next = client.getVariant(flagKey, contextRef.current);
|
|
60
|
+
setResult((prev) => (prev.variant === next.variant && prev.reason === next.reason ? prev : next));
|
|
61
|
+
};
|
|
62
|
+
update();
|
|
63
|
+
const unsubReady = client.on("PROVIDER_READY", update);
|
|
64
|
+
const unsubChange = client.on("PROVIDER_CONFIGURATION_CHANGED", update);
|
|
65
|
+
return () => { unsubReady(); unsubChange(); };
|
|
66
|
+
}, [client, flagKey]);
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { OmniFlagsProvider } from "./provider.js";
|
|
2
|
+
export type { OmniFlagsProviderProps } from "./provider.js";
|
|
3
|
+
export { AsyncStorageCache } from "./async-storage-cache.js";
|
|
4
|
+
export type { AsyncStorageLike, ReactNativeAsyncStorage } from "./async-storage-cache.js";
|
|
5
|
+
export { useOmniFlags, useFlag, useFlagValue, useFlagVariant, useFlagLoading } from "./hooks.js";
|
|
6
|
+
export { OmniFlagsContext } from "./context.js";
|
|
7
|
+
export type { OmniFlagsContextValue } from "./context.js";
|
|
8
|
+
export type { EvaluationContext, EvaluationResult, FlagConfig, FlagOrigin, Hook, LoadingState, ProviderEvent, } from "@omniretail/omniflags-core";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { OmniFlagsProvider } from "./provider.js";
|
|
2
|
+
export { AsyncStorageCache } from "./async-storage-cache.js";
|
|
3
|
+
export { useOmniFlags, useFlag, useFlagValue, useFlagVariant, useFlagLoading } from "./hooks.js";
|
|
4
|
+
export { OmniFlagsContext } from "./context.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React, { type ReactNode } from "react";
|
|
2
|
+
import { type ReactNativeAsyncStorage } from "./async-storage-cache.js";
|
|
3
|
+
export interface OmniFlagsProviderProps {
|
|
4
|
+
sdkKey: string;
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
/** Optional override for AsyncStorage implementation (tests/custom runtimes). */
|
|
7
|
+
asyncStorage?: ReactNativeAsyncStorage;
|
|
8
|
+
}
|
|
9
|
+
export declare function OmniFlagsProvider({ sdkKey, children, asyncStorage, ...rest }: OmniFlagsProviderProps): React.FunctionComponentElement<React.ProviderProps<import("./context.js").OmniFlagsContextValue | null>>;
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { AppState } from "react-native";
|
|
3
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
4
|
+
import { OmniFlagsClient } from "@omniretail/omniflags-core";
|
|
5
|
+
import { OmniFlagsContext } from "./context.js";
|
|
6
|
+
import { AsyncStorageCache } from "./async-storage-cache.js";
|
|
7
|
+
export function OmniFlagsProvider({ sdkKey, children, asyncStorage, ...rest }) {
|
|
8
|
+
const provider = rest.provider;
|
|
9
|
+
const [ready, setReady] = useState(false);
|
|
10
|
+
const clientRef = useRef(null);
|
|
11
|
+
if (!clientRef.current) {
|
|
12
|
+
clientRef.current = new OmniFlagsClient({ sdkKey, provider }, new AsyncStorageCache(asyncStorage ?? AsyncStorage));
|
|
13
|
+
}
|
|
14
|
+
const client = clientRef.current;
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
let mounted = true;
|
|
17
|
+
client.waitForReady().then(() => {
|
|
18
|
+
if (mounted)
|
|
19
|
+
setReady(true);
|
|
20
|
+
});
|
|
21
|
+
return () => {
|
|
22
|
+
mounted = false;
|
|
23
|
+
void client.shutdown();
|
|
24
|
+
};
|
|
25
|
+
}, [client]);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const subscription = AppState.addEventListener("change", (nextState) => {
|
|
28
|
+
if (nextState === "active") {
|
|
29
|
+
client.refresh();
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return () => {
|
|
33
|
+
subscription.remove();
|
|
34
|
+
};
|
|
35
|
+
}, [client]);
|
|
36
|
+
return React.createElement(OmniFlagsContext.Provider, { value: { client, ready } }, children);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAC3E,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,YAAY,MAAM,2CAA2C,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAgC,MAAM,0BAA0B,CAAC;AAc3F,MAAM,UAAU,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,GAAG,IAAI,EACgB;IACvB,MAAM,QAAQ,GAAI,IAAsB,CAAC,QAAQ,CAAC;IAClD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,MAAM,CAAyB,IAAI,CAAC,CAAC;IAEvD,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACvB,SAAS,CAAC,OAAO,GAAG,IAAI,eAAe,CACrC,EAAE,MAAM,EAAE,QAAQ,EAAE,EACpB,IAAI,iBAAiB,CAAC,YAAY,IAAI,YAAY,CAAC,CACpD,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC;IAEjC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,GAAG,IAAI,CAAC;QAEnB,MAAM,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;YAC9B,IAAI,OAAO;gBAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,OAAO,GAAG,KAAK,CAAC;YAChB,KAAK,MAAM,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,YAAY,GAAG,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,EAAE;YACrE,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;gBAC3B,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,OAAO,KAAK,CAAC,aAAa,CACxB,gBAAgB,CAAC,QAAQ,EACzB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAC5B,QAAQ,CACT,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@omniretail/omniflags-react-native",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "OmniFlags React Native SDK — provider + hooks with built-in AsyncStorage offline cache",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@react-native-async-storage/async-storage": ">=1.0.0",
|
|
24
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependenciesMeta": {
|
|
27
|
+
"@react-native-async-storage/async-storage": {
|
|
28
|
+
"optional": false
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@omniretail/omniflags-core": "file:../core"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@testing-library/react": "^16.0.0",
|
|
36
|
+
"@types/react": "^19.0.0",
|
|
37
|
+
"jsdom": "^29.0.0",
|
|
38
|
+
"react": "^19.0.0",
|
|
39
|
+
"react-dom": "^19.0.0",
|
|
40
|
+
"typescript": "^5.4.0",
|
|
41
|
+
"vitest": "^2.0.0"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"feature-flags",
|
|
48
|
+
"omniflags",
|
|
49
|
+
"react-native"
|
|
50
|
+
],
|
|
51
|
+
"license": "MIT"
|
|
52
|
+
}
|