@supashiphq/react-native-sdk 1.0.3
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/LICENSE +21 -0
- package/README.md +81 -0
- package/SECURITY.md +15 -0
- package/dist/index.d.mts +209 -0
- package/dist/index.d.ts +209 -0
- package/dist/index.js +106 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +69 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Supaship
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Supaship React Native SDK
|
|
2
|
+
|
|
3
|
+
React Native bindings for Supaship: the same hooks and patterns as [`@supashiphq/react-sdk`](https://www.npmjs.com/package/@supashiphq/react-sdk), without browser-only APIs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @supashiphq/react-native-sdk
|
|
9
|
+
# peers: react, react-native (@supashiphq/javascript-sdk is bundled as a dependency)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick start
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { SupaProvider, useFeature, FeaturesWithFallbacks } from '@supashiphq/react-native-sdk'
|
|
16
|
+
import { View, Text } from 'react-native'
|
|
17
|
+
|
|
18
|
+
const features = {
|
|
19
|
+
'new-header': false,
|
|
20
|
+
'theme-config': { mode: 'dark' as const, showLogo: true },
|
|
21
|
+
} satisfies FeaturesWithFallbacks
|
|
22
|
+
|
|
23
|
+
export function App() {
|
|
24
|
+
return (
|
|
25
|
+
<SupaProvider
|
|
26
|
+
config={{
|
|
27
|
+
sdkKey: 'your-sdk-key',
|
|
28
|
+
environment: 'production',
|
|
29
|
+
features,
|
|
30
|
+
context: { userId: '123' },
|
|
31
|
+
// Optional: disable toolbar explicitly (dev toolbar is web-only; native apps never show it)
|
|
32
|
+
toolbar: false,
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
<Home />
|
|
36
|
+
</SupaProvider>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function Home() {
|
|
41
|
+
const { feature: newHeader, isLoading } = useFeature('new-header')
|
|
42
|
+
if (isLoading) return <Text>Loading…</Text>
|
|
43
|
+
return <View>{newHeader ? <Text>New</Text> : <Text>Old</Text>}</View>
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Type-safe feature flags
|
|
48
|
+
|
|
49
|
+
Same module augmentation as the React SDK; use `@supashiphq/react-native-sdk` in your `declare module`:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { FeaturesWithFallbacks, InferFeatures } from '@supashiphq/react-native-sdk'
|
|
53
|
+
|
|
54
|
+
export const FEATURE_FLAGS = {
|
|
55
|
+
'new-header': false,
|
|
56
|
+
} satisfies FeaturesWithFallbacks
|
|
57
|
+
|
|
58
|
+
declare module '@supashiphq/react-native-sdk' {
|
|
59
|
+
interface Features extends InferFeatures<typeof FEATURE_FLAGS> {}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## API
|
|
64
|
+
|
|
65
|
+
Exports match the React SDK where applicable:
|
|
66
|
+
|
|
67
|
+
- `SupaProvider`, `useClient`, `useFeatureContext`
|
|
68
|
+
- `useFeature`, `useFeatures`
|
|
69
|
+
- `SupaFeature` (boolean-style variations; children are `ReactNode`, e.g. RN `View` / `Text`)
|
|
70
|
+
|
|
71
|
+
### Query behavior / `refetchOnWindowFocus`
|
|
72
|
+
|
|
73
|
+
The underlying `useQuery` option **`refetchOnWindowFocus`** keeps the same name as the web SDK. On React Native it triggers a refetch when the app moves to **`AppState` `active`** (foreground), not browser window focus. The default in `useFeature` / `useFeatures` remains `false`, same as React.
|
|
74
|
+
|
|
75
|
+
## Sensitive context hashing
|
|
76
|
+
|
|
77
|
+
If you use `sensitiveContextProperties` on the client, evaluation uses the Web Crypto API when available. In some Hermes/React Native setups you may need a `crypto.subtle` polyfill for hashing; feature fetching without that path is unaffected. See `@supashiphq/javascript-sdk` for details.
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT — see [LICENSE](./LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
The latest published minor version of `@supashiphq/react-native-sdk` is actively supported.
|
|
6
|
+
|
|
7
|
+
## Reporting a Vulnerability
|
|
8
|
+
|
|
9
|
+
If you discover a security issue, do not open a public GitHub issue.
|
|
10
|
+
|
|
11
|
+
- Email: `security@supaship.com`
|
|
12
|
+
- Include: impact, reproduction steps, and affected versions
|
|
13
|
+
- Expected response: acknowledgement within 3 business days
|
|
14
|
+
|
|
15
|
+
We will investigate, coordinate a fix, and publish a security release as needed.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import * as _supashiphq_react_core from '@supashiphq/react-core';
|
|
2
|
+
import { QueryState, UseFeaturesResult, ExtractFeatureFlagValue } from '@supashiphq/react-core';
|
|
3
|
+
export { UseFeatureResult, UseFeaturesResult } from '@supashiphq/react-core';
|
|
4
|
+
import * as react from 'react';
|
|
5
|
+
import react__default, { ReactNode } from 'react';
|
|
6
|
+
import * as _supashiphq_javascript_sdk from '@supashiphq/javascript-sdk';
|
|
7
|
+
import { FeatureValue, FeaturesWithFallbacks, FeatureContext } from '@supashiphq/javascript-sdk';
|
|
8
|
+
export { FeatureContext, FeatureValue, FeaturesWithFallbacks, SupaPlugin, SupaClientConfig as SupaProviderConfig, SupaToolbarPluginConfig, SupaToolbarPosition } from '@supashiphq/javascript-sdk';
|
|
9
|
+
|
|
10
|
+
declare const SupaProvider: <TFeatures extends _supashiphq_javascript_sdk.WidenFeatures<Record<string, _supashiphq_javascript_sdk.FeatureValue>>>({ config, toolbar, children, }: {
|
|
11
|
+
config: Omit<_supashiphq_javascript_sdk.SupaClientConfig, "plugins" | "toolbar"> & {
|
|
12
|
+
features: TFeatures;
|
|
13
|
+
};
|
|
14
|
+
toolbar?: boolean | _supashiphq_javascript_sdk.SupaToolbarPluginConfig | undefined;
|
|
15
|
+
children: react.ReactNode;
|
|
16
|
+
}) => react.JSX.Element;
|
|
17
|
+
declare const useClient: <TFeatures_1 extends _supashiphq_javascript_sdk.WidenFeatures<Record<string, _supashiphq_javascript_sdk.FeatureValue>>>() => _supashiphq_javascript_sdk.SupaClient<TFeatures_1>;
|
|
18
|
+
declare const useFeatureContext: () => {
|
|
19
|
+
updateContext: (context: _supashiphq_javascript_sdk.FeatureContext, mergeWithExisting?: boolean | undefined) => void;
|
|
20
|
+
getContext: () => _supashiphq_javascript_sdk.FeatureContext | undefined;
|
|
21
|
+
};
|
|
22
|
+
declare const useQuery: <TData = unknown, TError = Error>(queryKey: _supashiphq_react_core.QueryKey, queryFn: () => Promise<TData>, options?: _supashiphq_react_core.UseQueryOptions<TData, TError> | undefined) => _supashiphq_react_core.QueryState<TData, TError>;
|
|
23
|
+
declare const useQueryClient: () => {
|
|
24
|
+
_backing: {
|
|
25
|
+
_entries: Map<string, {
|
|
26
|
+
data: unknown;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
}>;
|
|
29
|
+
_timers: Map<string, NodeJS.Timeout>;
|
|
30
|
+
_observers: Map<string, Set<() => void>>;
|
|
31
|
+
getQuery(queryKey: string): unknown;
|
|
32
|
+
isStale(queryKey: string, staleTime: number): boolean;
|
|
33
|
+
setQuery(queryKey: string, data: unknown, cacheTime?: number | undefined): void;
|
|
34
|
+
subscribe(queryKey: string, callback: () => void): () => void;
|
|
35
|
+
notifyObservers(queryKey: string): void;
|
|
36
|
+
invalidateQuery(queryKey: string): void;
|
|
37
|
+
invalidateQueries(queryKeyPrefix: string): void;
|
|
38
|
+
clear(): void;
|
|
39
|
+
};
|
|
40
|
+
invalidateQueries(queryKey: _supashiphq_react_core.QueryKey): void;
|
|
41
|
+
invalidateQueriesByPrefix(queryKeyPrefix: _supashiphq_react_core.QueryKey): void;
|
|
42
|
+
clear(): void;
|
|
43
|
+
};
|
|
44
|
+
declare const queryCache: {
|
|
45
|
+
_entries: Map<string, {
|
|
46
|
+
data: unknown;
|
|
47
|
+
timestamp: number;
|
|
48
|
+
}>;
|
|
49
|
+
_timers: Map<string, NodeJS.Timeout>;
|
|
50
|
+
_observers: Map<string, Set<() => void>>;
|
|
51
|
+
getQuery(queryKey: string): unknown;
|
|
52
|
+
isStale(queryKey: string, staleTime: number): boolean;
|
|
53
|
+
setQuery(queryKey: string, data: unknown, cacheTime?: number | undefined): void;
|
|
54
|
+
subscribe(queryKey: string, callback: () => void): () => void;
|
|
55
|
+
notifyObservers(queryKey: string): void;
|
|
56
|
+
invalidateQuery(queryKey: string): void;
|
|
57
|
+
invalidateQueries(queryKeyPrefix: string): void;
|
|
58
|
+
clear(): void;
|
|
59
|
+
};
|
|
60
|
+
declare const QueryClient: new (cache?: {
|
|
61
|
+
_entries: Map<string, {
|
|
62
|
+
data: unknown;
|
|
63
|
+
timestamp: number;
|
|
64
|
+
}>;
|
|
65
|
+
_timers: Map<string, NodeJS.Timeout>;
|
|
66
|
+
_observers: Map<string, Set<() => void>>;
|
|
67
|
+
getQuery(queryKey: string): unknown;
|
|
68
|
+
isStale(queryKey: string, staleTime: number): boolean;
|
|
69
|
+
setQuery(queryKey: string, data: unknown, cacheTime?: number | undefined): void;
|
|
70
|
+
subscribe(queryKey: string, callback: () => void): () => void;
|
|
71
|
+
notifyObservers(queryKey: string): void;
|
|
72
|
+
invalidateQuery(queryKey: string): void;
|
|
73
|
+
invalidateQueries(queryKeyPrefix: string): void;
|
|
74
|
+
clear(): void;
|
|
75
|
+
} | undefined) => {
|
|
76
|
+
_backing: {
|
|
77
|
+
_entries: Map<string, {
|
|
78
|
+
data: unknown;
|
|
79
|
+
timestamp: number;
|
|
80
|
+
}>;
|
|
81
|
+
_timers: Map<string, NodeJS.Timeout>;
|
|
82
|
+
_observers: Map<string, Set<() => void>>;
|
|
83
|
+
getQuery(queryKey: string): unknown;
|
|
84
|
+
isStale(queryKey: string, staleTime: number): boolean;
|
|
85
|
+
setQuery(queryKey: string, data: unknown, cacheTime?: number | undefined): void;
|
|
86
|
+
subscribe(queryKey: string, callback: () => void): () => void;
|
|
87
|
+
notifyObservers(queryKey: string): void;
|
|
88
|
+
invalidateQuery(queryKey: string): void;
|
|
89
|
+
invalidateQueries(queryKeyPrefix: string): void;
|
|
90
|
+
clear(): void;
|
|
91
|
+
};
|
|
92
|
+
invalidateQueries(queryKey: _supashiphq_react_core.QueryKey): void;
|
|
93
|
+
invalidateQueriesByPrefix(queryKeyPrefix: _supashiphq_react_core.QueryKey): void;
|
|
94
|
+
clear(): void;
|
|
95
|
+
};
|
|
96
|
+
declare const getInitialQueryState: typeof _supashiphq_react_core.getInitialQueryState;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Helper type to infer feature types from your feature config for module augmentation.
|
|
100
|
+
*/
|
|
101
|
+
type InferFeatures<T extends FeaturesWithFallbacks> = {
|
|
102
|
+
[K in keyof T]: T[K];
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Interface to be augmented by users for type-safe feature flags.
|
|
106
|
+
* Use InferFeatures helper to enable type-safe feature access.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* declare module '@supashiphq/react-native-sdk' {
|
|
111
|
+
* interface Features extends InferFeatures<typeof FEATURE_FLAGS> {}
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
interface Features {
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Return type for useFeature hook with proper typing based on feature key
|
|
119
|
+
*/
|
|
120
|
+
type TypedFeatures = keyof Features extends never ? {
|
|
121
|
+
[key: string]: Omit<QueryState<FeatureValue | null>, 'data'> & {
|
|
122
|
+
feature: FeatureValue | null;
|
|
123
|
+
};
|
|
124
|
+
} : {
|
|
125
|
+
[K in keyof Features]: Omit<QueryState<Features[K]>, 'data'> & {
|
|
126
|
+
feature: Features[K];
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
type FeatureKey = keyof Features extends never ? string : keyof Features;
|
|
130
|
+
interface UseFeatureOptions {
|
|
131
|
+
context?: FeatureContext;
|
|
132
|
+
shouldFetch?: boolean;
|
|
133
|
+
}
|
|
134
|
+
interface UseFeaturesOptions {
|
|
135
|
+
context?: FeatureContext;
|
|
136
|
+
shouldFetch?: boolean;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Returns the state of a given feature for the current context.
|
|
141
|
+
*
|
|
142
|
+
* @remarks
|
|
143
|
+
* For type-safe feature flags, augment the Features interface:
|
|
144
|
+
* ```ts
|
|
145
|
+
* declare module '@supashiphq/react-native-sdk' {
|
|
146
|
+
* interface Features {
|
|
147
|
+
* 'my-feature': { value: 'variant-a' | 'variant-b' }
|
|
148
|
+
* 'dark-mode': { value: boolean }
|
|
149
|
+
* }
|
|
150
|
+
* }
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
declare function useFeature<TKey extends FeatureKey>(key: TKey, options?: {
|
|
154
|
+
context?: FeatureContext;
|
|
155
|
+
shouldFetch?: boolean;
|
|
156
|
+
}): TypedFeatures[TKey];
|
|
157
|
+
/**
|
|
158
|
+
* Returns the state of multiple features for the current context.
|
|
159
|
+
*/
|
|
160
|
+
declare function useFeatures<TKeys extends readonly FeatureKey[]>(featureNames: TKeys, options?: {
|
|
161
|
+
context?: FeatureContext;
|
|
162
|
+
shouldFetch?: boolean;
|
|
163
|
+
}): UseFeaturesResult<{
|
|
164
|
+
[K in TKeys[number]]: K extends keyof Features ? ExtractFeatureFlagValue<Features[K]> : FeatureValue | null;
|
|
165
|
+
}>;
|
|
166
|
+
|
|
167
|
+
interface SupaFeatureProps {
|
|
168
|
+
/**
|
|
169
|
+
* The feature flag key to evaluate
|
|
170
|
+
*/
|
|
171
|
+
feature: FeatureKey;
|
|
172
|
+
/**
|
|
173
|
+
* Context for feature evaluation
|
|
174
|
+
*/
|
|
175
|
+
context?: FeatureContext;
|
|
176
|
+
/**
|
|
177
|
+
* Whether to fetch the feature (default: true)
|
|
178
|
+
*/
|
|
179
|
+
shouldFetch?: boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Variations object mapping "true" or "false" to React nodes (e.g. RN Views)
|
|
182
|
+
*/
|
|
183
|
+
variations: {
|
|
184
|
+
true: ReactNode;
|
|
185
|
+
false?: ReactNode;
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Component to render during loading state
|
|
189
|
+
*/
|
|
190
|
+
loading?: ReactNode;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Conditionally renders variations based on boolean-style feature flag values.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```tsx
|
|
197
|
+
* <SupaFeature
|
|
198
|
+
* feature="new-header"
|
|
199
|
+
* loading={<ActivityIndicator />}
|
|
200
|
+
* variations={{
|
|
201
|
+
* true: <NewHeader />,
|
|
202
|
+
* false: <OldHeader />
|
|
203
|
+
* }}
|
|
204
|
+
* />
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
declare function SupaFeature({ feature, context, shouldFetch, variations, loading, }: SupaFeatureProps): react__default.JSX.Element | null;
|
|
208
|
+
|
|
209
|
+
export { type FeatureKey, type Features, type InferFeatures, QueryClient, SupaFeature, type SupaFeatureProps, SupaProvider, type TypedFeatures, type UseFeatureOptions, type UseFeaturesOptions, getInitialQueryState, queryCache, useClient, useFeature, useFeatureContext, useFeatures, useQuery, useQueryClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import * as _supashiphq_react_core from '@supashiphq/react-core';
|
|
2
|
+
import { QueryState, UseFeaturesResult, ExtractFeatureFlagValue } from '@supashiphq/react-core';
|
|
3
|
+
export { UseFeatureResult, UseFeaturesResult } from '@supashiphq/react-core';
|
|
4
|
+
import * as react from 'react';
|
|
5
|
+
import react__default, { ReactNode } from 'react';
|
|
6
|
+
import * as _supashiphq_javascript_sdk from '@supashiphq/javascript-sdk';
|
|
7
|
+
import { FeatureValue, FeaturesWithFallbacks, FeatureContext } from '@supashiphq/javascript-sdk';
|
|
8
|
+
export { FeatureContext, FeatureValue, FeaturesWithFallbacks, SupaPlugin, SupaClientConfig as SupaProviderConfig, SupaToolbarPluginConfig, SupaToolbarPosition } from '@supashiphq/javascript-sdk';
|
|
9
|
+
|
|
10
|
+
declare const SupaProvider: <TFeatures extends _supashiphq_javascript_sdk.WidenFeatures<Record<string, _supashiphq_javascript_sdk.FeatureValue>>>({ config, toolbar, children, }: {
|
|
11
|
+
config: Omit<_supashiphq_javascript_sdk.SupaClientConfig, "plugins" | "toolbar"> & {
|
|
12
|
+
features: TFeatures;
|
|
13
|
+
};
|
|
14
|
+
toolbar?: boolean | _supashiphq_javascript_sdk.SupaToolbarPluginConfig | undefined;
|
|
15
|
+
children: react.ReactNode;
|
|
16
|
+
}) => react.JSX.Element;
|
|
17
|
+
declare const useClient: <TFeatures_1 extends _supashiphq_javascript_sdk.WidenFeatures<Record<string, _supashiphq_javascript_sdk.FeatureValue>>>() => _supashiphq_javascript_sdk.SupaClient<TFeatures_1>;
|
|
18
|
+
declare const useFeatureContext: () => {
|
|
19
|
+
updateContext: (context: _supashiphq_javascript_sdk.FeatureContext, mergeWithExisting?: boolean | undefined) => void;
|
|
20
|
+
getContext: () => _supashiphq_javascript_sdk.FeatureContext | undefined;
|
|
21
|
+
};
|
|
22
|
+
declare const useQuery: <TData = unknown, TError = Error>(queryKey: _supashiphq_react_core.QueryKey, queryFn: () => Promise<TData>, options?: _supashiphq_react_core.UseQueryOptions<TData, TError> | undefined) => _supashiphq_react_core.QueryState<TData, TError>;
|
|
23
|
+
declare const useQueryClient: () => {
|
|
24
|
+
_backing: {
|
|
25
|
+
_entries: Map<string, {
|
|
26
|
+
data: unknown;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
}>;
|
|
29
|
+
_timers: Map<string, NodeJS.Timeout>;
|
|
30
|
+
_observers: Map<string, Set<() => void>>;
|
|
31
|
+
getQuery(queryKey: string): unknown;
|
|
32
|
+
isStale(queryKey: string, staleTime: number): boolean;
|
|
33
|
+
setQuery(queryKey: string, data: unknown, cacheTime?: number | undefined): void;
|
|
34
|
+
subscribe(queryKey: string, callback: () => void): () => void;
|
|
35
|
+
notifyObservers(queryKey: string): void;
|
|
36
|
+
invalidateQuery(queryKey: string): void;
|
|
37
|
+
invalidateQueries(queryKeyPrefix: string): void;
|
|
38
|
+
clear(): void;
|
|
39
|
+
};
|
|
40
|
+
invalidateQueries(queryKey: _supashiphq_react_core.QueryKey): void;
|
|
41
|
+
invalidateQueriesByPrefix(queryKeyPrefix: _supashiphq_react_core.QueryKey): void;
|
|
42
|
+
clear(): void;
|
|
43
|
+
};
|
|
44
|
+
declare const queryCache: {
|
|
45
|
+
_entries: Map<string, {
|
|
46
|
+
data: unknown;
|
|
47
|
+
timestamp: number;
|
|
48
|
+
}>;
|
|
49
|
+
_timers: Map<string, NodeJS.Timeout>;
|
|
50
|
+
_observers: Map<string, Set<() => void>>;
|
|
51
|
+
getQuery(queryKey: string): unknown;
|
|
52
|
+
isStale(queryKey: string, staleTime: number): boolean;
|
|
53
|
+
setQuery(queryKey: string, data: unknown, cacheTime?: number | undefined): void;
|
|
54
|
+
subscribe(queryKey: string, callback: () => void): () => void;
|
|
55
|
+
notifyObservers(queryKey: string): void;
|
|
56
|
+
invalidateQuery(queryKey: string): void;
|
|
57
|
+
invalidateQueries(queryKeyPrefix: string): void;
|
|
58
|
+
clear(): void;
|
|
59
|
+
};
|
|
60
|
+
declare const QueryClient: new (cache?: {
|
|
61
|
+
_entries: Map<string, {
|
|
62
|
+
data: unknown;
|
|
63
|
+
timestamp: number;
|
|
64
|
+
}>;
|
|
65
|
+
_timers: Map<string, NodeJS.Timeout>;
|
|
66
|
+
_observers: Map<string, Set<() => void>>;
|
|
67
|
+
getQuery(queryKey: string): unknown;
|
|
68
|
+
isStale(queryKey: string, staleTime: number): boolean;
|
|
69
|
+
setQuery(queryKey: string, data: unknown, cacheTime?: number | undefined): void;
|
|
70
|
+
subscribe(queryKey: string, callback: () => void): () => void;
|
|
71
|
+
notifyObservers(queryKey: string): void;
|
|
72
|
+
invalidateQuery(queryKey: string): void;
|
|
73
|
+
invalidateQueries(queryKeyPrefix: string): void;
|
|
74
|
+
clear(): void;
|
|
75
|
+
} | undefined) => {
|
|
76
|
+
_backing: {
|
|
77
|
+
_entries: Map<string, {
|
|
78
|
+
data: unknown;
|
|
79
|
+
timestamp: number;
|
|
80
|
+
}>;
|
|
81
|
+
_timers: Map<string, NodeJS.Timeout>;
|
|
82
|
+
_observers: Map<string, Set<() => void>>;
|
|
83
|
+
getQuery(queryKey: string): unknown;
|
|
84
|
+
isStale(queryKey: string, staleTime: number): boolean;
|
|
85
|
+
setQuery(queryKey: string, data: unknown, cacheTime?: number | undefined): void;
|
|
86
|
+
subscribe(queryKey: string, callback: () => void): () => void;
|
|
87
|
+
notifyObservers(queryKey: string): void;
|
|
88
|
+
invalidateQuery(queryKey: string): void;
|
|
89
|
+
invalidateQueries(queryKeyPrefix: string): void;
|
|
90
|
+
clear(): void;
|
|
91
|
+
};
|
|
92
|
+
invalidateQueries(queryKey: _supashiphq_react_core.QueryKey): void;
|
|
93
|
+
invalidateQueriesByPrefix(queryKeyPrefix: _supashiphq_react_core.QueryKey): void;
|
|
94
|
+
clear(): void;
|
|
95
|
+
};
|
|
96
|
+
declare const getInitialQueryState: typeof _supashiphq_react_core.getInitialQueryState;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Helper type to infer feature types from your feature config for module augmentation.
|
|
100
|
+
*/
|
|
101
|
+
type InferFeatures<T extends FeaturesWithFallbacks> = {
|
|
102
|
+
[K in keyof T]: T[K];
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Interface to be augmented by users for type-safe feature flags.
|
|
106
|
+
* Use InferFeatures helper to enable type-safe feature access.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* declare module '@supashiphq/react-native-sdk' {
|
|
111
|
+
* interface Features extends InferFeatures<typeof FEATURE_FLAGS> {}
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
interface Features {
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Return type for useFeature hook with proper typing based on feature key
|
|
119
|
+
*/
|
|
120
|
+
type TypedFeatures = keyof Features extends never ? {
|
|
121
|
+
[key: string]: Omit<QueryState<FeatureValue | null>, 'data'> & {
|
|
122
|
+
feature: FeatureValue | null;
|
|
123
|
+
};
|
|
124
|
+
} : {
|
|
125
|
+
[K in keyof Features]: Omit<QueryState<Features[K]>, 'data'> & {
|
|
126
|
+
feature: Features[K];
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
type FeatureKey = keyof Features extends never ? string : keyof Features;
|
|
130
|
+
interface UseFeatureOptions {
|
|
131
|
+
context?: FeatureContext;
|
|
132
|
+
shouldFetch?: boolean;
|
|
133
|
+
}
|
|
134
|
+
interface UseFeaturesOptions {
|
|
135
|
+
context?: FeatureContext;
|
|
136
|
+
shouldFetch?: boolean;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Returns the state of a given feature for the current context.
|
|
141
|
+
*
|
|
142
|
+
* @remarks
|
|
143
|
+
* For type-safe feature flags, augment the Features interface:
|
|
144
|
+
* ```ts
|
|
145
|
+
* declare module '@supashiphq/react-native-sdk' {
|
|
146
|
+
* interface Features {
|
|
147
|
+
* 'my-feature': { value: 'variant-a' | 'variant-b' }
|
|
148
|
+
* 'dark-mode': { value: boolean }
|
|
149
|
+
* }
|
|
150
|
+
* }
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
declare function useFeature<TKey extends FeatureKey>(key: TKey, options?: {
|
|
154
|
+
context?: FeatureContext;
|
|
155
|
+
shouldFetch?: boolean;
|
|
156
|
+
}): TypedFeatures[TKey];
|
|
157
|
+
/**
|
|
158
|
+
* Returns the state of multiple features for the current context.
|
|
159
|
+
*/
|
|
160
|
+
declare function useFeatures<TKeys extends readonly FeatureKey[]>(featureNames: TKeys, options?: {
|
|
161
|
+
context?: FeatureContext;
|
|
162
|
+
shouldFetch?: boolean;
|
|
163
|
+
}): UseFeaturesResult<{
|
|
164
|
+
[K in TKeys[number]]: K extends keyof Features ? ExtractFeatureFlagValue<Features[K]> : FeatureValue | null;
|
|
165
|
+
}>;
|
|
166
|
+
|
|
167
|
+
interface SupaFeatureProps {
|
|
168
|
+
/**
|
|
169
|
+
* The feature flag key to evaluate
|
|
170
|
+
*/
|
|
171
|
+
feature: FeatureKey;
|
|
172
|
+
/**
|
|
173
|
+
* Context for feature evaluation
|
|
174
|
+
*/
|
|
175
|
+
context?: FeatureContext;
|
|
176
|
+
/**
|
|
177
|
+
* Whether to fetch the feature (default: true)
|
|
178
|
+
*/
|
|
179
|
+
shouldFetch?: boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Variations object mapping "true" or "false" to React nodes (e.g. RN Views)
|
|
182
|
+
*/
|
|
183
|
+
variations: {
|
|
184
|
+
true: ReactNode;
|
|
185
|
+
false?: ReactNode;
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Component to render during loading state
|
|
189
|
+
*/
|
|
190
|
+
loading?: ReactNode;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Conditionally renders variations based on boolean-style feature flag values.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```tsx
|
|
197
|
+
* <SupaFeature
|
|
198
|
+
* feature="new-header"
|
|
199
|
+
* loading={<ActivityIndicator />}
|
|
200
|
+
* variations={{
|
|
201
|
+
* true: <NewHeader />,
|
|
202
|
+
* false: <OldHeader />
|
|
203
|
+
* }}
|
|
204
|
+
* />
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
declare function SupaFeature({ feature, context, shouldFetch, variations, loading, }: SupaFeatureProps): react__default.JSX.Element | null;
|
|
208
|
+
|
|
209
|
+
export { type FeatureKey, type Features, type InferFeatures, QueryClient, SupaFeature, type SupaFeatureProps, SupaProvider, type TypedFeatures, type UseFeatureOptions, type UseFeaturesOptions, getInitialQueryState, queryCache, useClient, useFeature, useFeatureContext, useFeatures, useQuery, useQueryClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
QueryClient: () => QueryClient,
|
|
24
|
+
SupaFeature: () => SupaFeature,
|
|
25
|
+
SupaProvider: () => SupaProvider,
|
|
26
|
+
getInitialQueryState: () => getInitialQueryState,
|
|
27
|
+
queryCache: () => queryCache,
|
|
28
|
+
useClient: () => useClient,
|
|
29
|
+
useFeature: () => useFeature,
|
|
30
|
+
useFeatureContext: () => useFeatureContext,
|
|
31
|
+
useFeatures: () => useFeatures,
|
|
32
|
+
useQuery: () => useQuery,
|
|
33
|
+
useQueryClient: () => useQueryClient
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/supaship.ts
|
|
38
|
+
var import_react_core = require("@supashiphq/react-core");
|
|
39
|
+
var import_focus_native = require("@supashiphq/react-core/focus-native");
|
|
40
|
+
var supaship = (0, import_react_core.createSupashipReact)(import_focus_native.useNativeFocusRefetchEffect);
|
|
41
|
+
var {
|
|
42
|
+
SupaProvider,
|
|
43
|
+
useClient,
|
|
44
|
+
useFeatureContext,
|
|
45
|
+
useQuery,
|
|
46
|
+
useQueryClient,
|
|
47
|
+
queryCache,
|
|
48
|
+
QueryClient,
|
|
49
|
+
getInitialQueryState
|
|
50
|
+
} = supaship;
|
|
51
|
+
var useFeatureUntyped = supaship.useFeature;
|
|
52
|
+
var useFeaturesUntyped = supaship.useFeatures;
|
|
53
|
+
|
|
54
|
+
// src/hooks.ts
|
|
55
|
+
function useFeature(key, options) {
|
|
56
|
+
return useFeatureUntyped(key, options);
|
|
57
|
+
}
|
|
58
|
+
function useFeatures(featureNames, options) {
|
|
59
|
+
return useFeaturesUntyped(featureNames, options);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/utils.ts
|
|
63
|
+
function hasValue(value) {
|
|
64
|
+
return value !== void 0 && value !== null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/components.tsx
|
|
68
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
69
|
+
function SupaFeature({
|
|
70
|
+
feature,
|
|
71
|
+
context,
|
|
72
|
+
shouldFetch = true,
|
|
73
|
+
variations,
|
|
74
|
+
loading
|
|
75
|
+
}) {
|
|
76
|
+
const { feature: featureValue, isLoading } = useFeature(feature, {
|
|
77
|
+
context,
|
|
78
|
+
shouldFetch
|
|
79
|
+
});
|
|
80
|
+
if (isLoading) {
|
|
81
|
+
return loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loading }) : null;
|
|
82
|
+
}
|
|
83
|
+
const valueKey = String(featureValue);
|
|
84
|
+
if (variations.true && valueKey === "true") {
|
|
85
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: variations.true });
|
|
86
|
+
}
|
|
87
|
+
if (variations.false && (valueKey === "false" || !hasValue(featureValue))) {
|
|
88
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: variations.false });
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
93
|
+
0 && (module.exports = {
|
|
94
|
+
QueryClient,
|
|
95
|
+
SupaFeature,
|
|
96
|
+
SupaProvider,
|
|
97
|
+
getInitialQueryState,
|
|
98
|
+
queryCache,
|
|
99
|
+
useClient,
|
|
100
|
+
useFeature,
|
|
101
|
+
useFeatureContext,
|
|
102
|
+
useFeatures,
|
|
103
|
+
useQuery,
|
|
104
|
+
useQueryClient
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/supaship.ts","../src/hooks.ts","../src/utils.ts","../src/components.tsx"],"sourcesContent":["export {\n SupaProvider,\n useClient,\n useFeatureContext,\n useQuery,\n useQueryClient,\n queryCache,\n QueryClient,\n getInitialQueryState,\n} from './supaship'\nexport * from './hooks'\nexport * from './types'\nexport * from './components'\n","import { createSupashipReact } from '@supashiphq/react-core'\nimport { useNativeFocusRefetchEffect } from '@supashiphq/react-core/focus-native'\n\nconst supaship = createSupashipReact(useNativeFocusRefetchEffect)\n\nexport const {\n SupaProvider,\n useClient,\n useFeatureContext,\n useQuery,\n useQueryClient,\n queryCache,\n QueryClient,\n getInitialQueryState,\n} = supaship\n\n/** @internal widened hooks — use `useFeature` / `useFeatures` from this package’s public API */\nexport const useFeatureUntyped = supaship.useFeature\n/** @internal */\nexport const useFeaturesUntyped = supaship.useFeatures\n","import { FeatureValue, FeatureContext } from '@supashiphq/javascript-sdk'\nimport type { ExtractFeatureFlagValue, UseFeaturesResult } from '@supashiphq/react-core'\nimport { useFeatureUntyped, useFeaturesUntyped } from './supaship'\nimport { FeatureKey, TypedFeatures, Features } from './types'\n\n/**\n * Returns the state of a given feature for the current context.\n *\n * @remarks\n * For type-safe feature flags, augment the Features interface:\n * ```ts\n * declare module '@supashiphq/react-native-sdk' {\n * interface Features {\n * 'my-feature': { value: 'variant-a' | 'variant-b' }\n * 'dark-mode': { value: boolean }\n * }\n * }\n * ```\n */\nexport function useFeature<TKey extends FeatureKey>(\n key: TKey,\n options?: { context?: FeatureContext; shouldFetch?: boolean }\n): TypedFeatures[TKey] {\n return useFeatureUntyped(key as string, options) as TypedFeatures[TKey]\n}\n\n/**\n * Returns the state of multiple features for the current context.\n */\nexport function useFeatures<TKeys extends readonly FeatureKey[]>(\n featureNames: TKeys,\n options?: {\n context?: FeatureContext\n shouldFetch?: boolean\n }\n): UseFeaturesResult<{\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureFlagValue<Features[K]>\n : FeatureValue | null\n}> {\n return useFeaturesUntyped(featureNames as readonly string[], options) as UseFeaturesResult<{\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureFlagValue<Features[K]>\n : FeatureValue | null\n }>\n}\n","export function hasValue(value: unknown): boolean {\n return value !== undefined && value !== null\n}\n","import React, { ReactNode } from 'react'\nimport { useFeature } from './hooks'\nimport { FeatureKey, FeatureContext } from './types'\nimport { hasValue } from './utils'\n\nexport interface SupaFeatureProps {\n /**\n * The feature flag key to evaluate\n */\n feature: FeatureKey\n\n /**\n * Context for feature evaluation\n */\n context?: FeatureContext\n\n /**\n * Whether to fetch the feature (default: true)\n */\n shouldFetch?: boolean\n\n /**\n * Variations object mapping \"true\" or \"false\" to React nodes (e.g. RN Views)\n */\n variations: {\n true: ReactNode\n false?: ReactNode\n }\n\n /**\n * Component to render during loading state\n */\n loading?: ReactNode\n}\n\n/**\n * Conditionally renders variations based on boolean-style feature flag values.\n *\n * @example\n * ```tsx\n * <SupaFeature\n * feature=\"new-header\"\n * loading={<ActivityIndicator />}\n * variations={{\n * true: <NewHeader />,\n * false: <OldHeader />\n * }}\n * />\n * ```\n */\nexport function SupaFeature({\n feature,\n context,\n shouldFetch = true,\n variations,\n loading,\n}: SupaFeatureProps): React.JSX.Element | null {\n const { feature: featureValue, isLoading } = useFeature(feature, {\n context,\n shouldFetch,\n })\n\n if (isLoading) {\n return loading ? <>{loading}</> : null\n }\n\n const valueKey = String(featureValue)\n\n if (variations.true && valueKey === 'true') {\n return <>{variations.true}</>\n }\n\n if (variations.false && (valueKey === 'false' || !hasValue(featureValue))) {\n return <>{variations.false}</>\n }\n\n return null\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,wBAAoC;AACpC,0BAA4C;AAE5C,IAAM,eAAW,uCAAoB,+CAA2B;AAEzD,IAAM;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,IAAI;AAGG,IAAM,oBAAoB,SAAS;AAEnC,IAAM,qBAAqB,SAAS;;;ACApC,SAAS,WACd,KACA,SACqB;AACrB,SAAO,kBAAkB,KAAe,OAAO;AACjD;AAKO,SAAS,YACd,cACA,SAQC;AACD,SAAO,mBAAmB,cAAmC,OAAO;AAKtE;;;AC7CO,SAAS,SAAS,OAAyB;AAChD,SAAO,UAAU,UAAa,UAAU;AAC1C;;;AC6DqB;AAbd,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AACF,GAA+C;AAC7C,QAAM,EAAE,SAAS,cAAc,UAAU,IAAI,WAAW,SAAS;AAAA,IAC/D;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,WAAW;AACb,WAAO,UAAU,2EAAG,mBAAQ,IAAM;AAAA,EACpC;AAEA,QAAM,WAAW,OAAO,YAAY;AAEpC,MAAI,WAAW,QAAQ,aAAa,QAAQ;AAC1C,WAAO,2EAAG,qBAAW,MAAK;AAAA,EAC5B;AAEA,MAAI,WAAW,UAAU,aAAa,WAAW,CAAC,SAAS,YAAY,IAAI;AACzE,WAAO,2EAAG,qBAAW,OAAM;AAAA,EAC7B;AAEA,SAAO;AACT;","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// src/supaship.ts
|
|
2
|
+
import { createSupashipReact } from "@supashiphq/react-core";
|
|
3
|
+
import { useNativeFocusRefetchEffect } from "@supashiphq/react-core/focus-native";
|
|
4
|
+
var supaship = createSupashipReact(useNativeFocusRefetchEffect);
|
|
5
|
+
var {
|
|
6
|
+
SupaProvider,
|
|
7
|
+
useClient,
|
|
8
|
+
useFeatureContext,
|
|
9
|
+
useQuery,
|
|
10
|
+
useQueryClient,
|
|
11
|
+
queryCache,
|
|
12
|
+
QueryClient,
|
|
13
|
+
getInitialQueryState
|
|
14
|
+
} = supaship;
|
|
15
|
+
var useFeatureUntyped = supaship.useFeature;
|
|
16
|
+
var useFeaturesUntyped = supaship.useFeatures;
|
|
17
|
+
|
|
18
|
+
// src/hooks.ts
|
|
19
|
+
function useFeature(key, options) {
|
|
20
|
+
return useFeatureUntyped(key, options);
|
|
21
|
+
}
|
|
22
|
+
function useFeatures(featureNames, options) {
|
|
23
|
+
return useFeaturesUntyped(featureNames, options);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/utils.ts
|
|
27
|
+
function hasValue(value) {
|
|
28
|
+
return value !== void 0 && value !== null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/components.tsx
|
|
32
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
33
|
+
function SupaFeature({
|
|
34
|
+
feature,
|
|
35
|
+
context,
|
|
36
|
+
shouldFetch = true,
|
|
37
|
+
variations,
|
|
38
|
+
loading
|
|
39
|
+
}) {
|
|
40
|
+
const { feature: featureValue, isLoading } = useFeature(feature, {
|
|
41
|
+
context,
|
|
42
|
+
shouldFetch
|
|
43
|
+
});
|
|
44
|
+
if (isLoading) {
|
|
45
|
+
return loading ? /* @__PURE__ */ jsx(Fragment, { children: loading }) : null;
|
|
46
|
+
}
|
|
47
|
+
const valueKey = String(featureValue);
|
|
48
|
+
if (variations.true && valueKey === "true") {
|
|
49
|
+
return /* @__PURE__ */ jsx(Fragment, { children: variations.true });
|
|
50
|
+
}
|
|
51
|
+
if (variations.false && (valueKey === "false" || !hasValue(featureValue))) {
|
|
52
|
+
return /* @__PURE__ */ jsx(Fragment, { children: variations.false });
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
QueryClient,
|
|
58
|
+
SupaFeature,
|
|
59
|
+
SupaProvider,
|
|
60
|
+
getInitialQueryState,
|
|
61
|
+
queryCache,
|
|
62
|
+
useClient,
|
|
63
|
+
useFeature,
|
|
64
|
+
useFeatureContext,
|
|
65
|
+
useFeatures,
|
|
66
|
+
useQuery,
|
|
67
|
+
useQueryClient
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/supaship.ts","../src/hooks.ts","../src/utils.ts","../src/components.tsx"],"sourcesContent":["import { createSupashipReact } from '@supashiphq/react-core'\nimport { useNativeFocusRefetchEffect } from '@supashiphq/react-core/focus-native'\n\nconst supaship = createSupashipReact(useNativeFocusRefetchEffect)\n\nexport const {\n SupaProvider,\n useClient,\n useFeatureContext,\n useQuery,\n useQueryClient,\n queryCache,\n QueryClient,\n getInitialQueryState,\n} = supaship\n\n/** @internal widened hooks — use `useFeature` / `useFeatures` from this package’s public API */\nexport const useFeatureUntyped = supaship.useFeature\n/** @internal */\nexport const useFeaturesUntyped = supaship.useFeatures\n","import { FeatureValue, FeatureContext } from '@supashiphq/javascript-sdk'\nimport type { ExtractFeatureFlagValue, UseFeaturesResult } from '@supashiphq/react-core'\nimport { useFeatureUntyped, useFeaturesUntyped } from './supaship'\nimport { FeatureKey, TypedFeatures, Features } from './types'\n\n/**\n * Returns the state of a given feature for the current context.\n *\n * @remarks\n * For type-safe feature flags, augment the Features interface:\n * ```ts\n * declare module '@supashiphq/react-native-sdk' {\n * interface Features {\n * 'my-feature': { value: 'variant-a' | 'variant-b' }\n * 'dark-mode': { value: boolean }\n * }\n * }\n * ```\n */\nexport function useFeature<TKey extends FeatureKey>(\n key: TKey,\n options?: { context?: FeatureContext; shouldFetch?: boolean }\n): TypedFeatures[TKey] {\n return useFeatureUntyped(key as string, options) as TypedFeatures[TKey]\n}\n\n/**\n * Returns the state of multiple features for the current context.\n */\nexport function useFeatures<TKeys extends readonly FeatureKey[]>(\n featureNames: TKeys,\n options?: {\n context?: FeatureContext\n shouldFetch?: boolean\n }\n): UseFeaturesResult<{\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureFlagValue<Features[K]>\n : FeatureValue | null\n}> {\n return useFeaturesUntyped(featureNames as readonly string[], options) as UseFeaturesResult<{\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureFlagValue<Features[K]>\n : FeatureValue | null\n }>\n}\n","export function hasValue(value: unknown): boolean {\n return value !== undefined && value !== null\n}\n","import React, { ReactNode } from 'react'\nimport { useFeature } from './hooks'\nimport { FeatureKey, FeatureContext } from './types'\nimport { hasValue } from './utils'\n\nexport interface SupaFeatureProps {\n /**\n * The feature flag key to evaluate\n */\n feature: FeatureKey\n\n /**\n * Context for feature evaluation\n */\n context?: FeatureContext\n\n /**\n * Whether to fetch the feature (default: true)\n */\n shouldFetch?: boolean\n\n /**\n * Variations object mapping \"true\" or \"false\" to React nodes (e.g. RN Views)\n */\n variations: {\n true: ReactNode\n false?: ReactNode\n }\n\n /**\n * Component to render during loading state\n */\n loading?: ReactNode\n}\n\n/**\n * Conditionally renders variations based on boolean-style feature flag values.\n *\n * @example\n * ```tsx\n * <SupaFeature\n * feature=\"new-header\"\n * loading={<ActivityIndicator />}\n * variations={{\n * true: <NewHeader />,\n * false: <OldHeader />\n * }}\n * />\n * ```\n */\nexport function SupaFeature({\n feature,\n context,\n shouldFetch = true,\n variations,\n loading,\n}: SupaFeatureProps): React.JSX.Element | null {\n const { feature: featureValue, isLoading } = useFeature(feature, {\n context,\n shouldFetch,\n })\n\n if (isLoading) {\n return loading ? <>{loading}</> : null\n }\n\n const valueKey = String(featureValue)\n\n if (variations.true && valueKey === 'true') {\n return <>{variations.true}</>\n }\n\n if (variations.false && (valueKey === 'false' || !hasValue(featureValue))) {\n return <>{variations.false}</>\n }\n\n return null\n}\n"],"mappings":";AAAA,SAAS,2BAA2B;AACpC,SAAS,mCAAmC;AAE5C,IAAM,WAAW,oBAAoB,2BAA2B;AAEzD,IAAM;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,IAAI;AAGG,IAAM,oBAAoB,SAAS;AAEnC,IAAM,qBAAqB,SAAS;;;ACApC,SAAS,WACd,KACA,SACqB;AACrB,SAAO,kBAAkB,KAAe,OAAO;AACjD;AAKO,SAAS,YACd,cACA,SAQC;AACD,SAAO,mBAAmB,cAAmC,OAAO;AAKtE;;;AC7CO,SAAS,SAAS,OAAyB;AAChD,SAAO,UAAU,UAAa,UAAU;AAC1C;;;AC6DqB;AAbd,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AACF,GAA+C;AAC7C,QAAM,EAAE,SAAS,cAAc,UAAU,IAAI,WAAW,SAAS;AAAA,IAC/D;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,WAAW;AACb,WAAO,UAAU,gCAAG,mBAAQ,IAAM;AAAA,EACpC;AAEA,QAAM,WAAW,OAAO,YAAY;AAEpC,MAAI,WAAW,QAAQ,aAAa,QAAQ;AAC1C,WAAO,gCAAG,qBAAW,MAAK;AAAA,EAC5B;AAEA,MAAI,WAAW,UAAU,aAAa,WAAW,CAAC,SAAS,YAAY,IAAI;AACzE,WAAO,gCAAG,qBAAW,OAAM;AAAA,EAC7B;AAEA,SAAO;AACT;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@supashiphq/react-native-sdk",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Supaship SDK for React Native",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"SECURITY.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"clean": "rimraf dist",
|
|
23
|
+
"build": "pnpm clean && pnpm build:tsup",
|
|
24
|
+
"build:tsup": "tsup",
|
|
25
|
+
"dev": "tsup --watch",
|
|
26
|
+
"test": "jest --watchAll=false",
|
|
27
|
+
"test:watch": "jest --watch",
|
|
28
|
+
"test:coverage": "jest --coverage",
|
|
29
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
30
|
+
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
31
|
+
"prepublishOnly": "pnpm build"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": ">=16.8.0",
|
|
35
|
+
"react-native": ">=0.71.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@supashiphq/javascript-sdk": "workspace:^1.0.3",
|
|
39
|
+
"@supashiphq/react-core": "workspace:^1.0.3"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@jest/globals": "^30.0.0",
|
|
43
|
+
"@testing-library/jest-dom": "^6.4.2",
|
|
44
|
+
"@testing-library/react": "^14.2.1",
|
|
45
|
+
"@types/jest": "^29.5.14",
|
|
46
|
+
"@types/node": "^20.0.0",
|
|
47
|
+
"@types/react": "^18.2.0",
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
49
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
50
|
+
"eslint": "^8.0.0",
|
|
51
|
+
"jest": "^29.7.0",
|
|
52
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
53
|
+
"react": "^18.2.0",
|
|
54
|
+
"rimraf": "^6.0.1",
|
|
55
|
+
"ts-jest": "^29.1.0",
|
|
56
|
+
"tsup": "^8.5.0",
|
|
57
|
+
"typescript": "~5.3.3"
|
|
58
|
+
},
|
|
59
|
+
"keywords": [
|
|
60
|
+
"feature-flags",
|
|
61
|
+
"feature-toggles",
|
|
62
|
+
"react-native",
|
|
63
|
+
"sdk"
|
|
64
|
+
],
|
|
65
|
+
"author": "Supaship",
|
|
66
|
+
"license": "MIT",
|
|
67
|
+
"repository": {
|
|
68
|
+
"type": "git",
|
|
69
|
+
"url": "https://github.com/SupashipHQ/javascript-sdk",
|
|
70
|
+
"directory": "packages/react-native"
|
|
71
|
+
},
|
|
72
|
+
"bugs": {
|
|
73
|
+
"url": "https://github.com/SupashipHQ/javascript-sdk/issues"
|
|
74
|
+
},
|
|
75
|
+
"homepage": "https://supaship.com",
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public",
|
|
78
|
+
"provenance": true
|
|
79
|
+
}
|
|
80
|
+
}
|