flipflag-sdk 1.1.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 +713 -0
- package/dist/core/FlipFlagSDK.js +379 -0
- package/dist/index.js +384 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +382 -0
- package/dist/index.mjs.map +1 -0
- package/dist/js/featureFlagManager.js +172 -0
- package/dist/js/identifyUser.js +15 -0
- package/dist/js/index.js +3 -0
- package/dist/js.js +565 -0
- package/dist/js.js.map +1 -0
- package/dist/js.mjs +561 -0
- package/dist/js.mjs.map +1 -0
- package/dist/next/FlipflagProvider.js +27 -0
- package/dist/next/identifyUser.js +9 -0
- package/dist/next/index.js +6 -0
- package/dist/next/server-utils.js +70 -0
- package/dist/next/types.js +1 -0
- package/dist/next/useABTest.js +53 -0
- package/dist/next/useFeatureFlag.js +55 -0
- package/dist/next/useFeatureFlags.js +69 -0
- package/dist/next.js +657 -0
- package/dist/next.js.map +1 -0
- package/dist/next.mjs +650 -0
- package/dist/next.mjs.map +1 -0
- package/dist/react/FeatureFlagProvider.js +19 -0
- package/dist/react/identifyUser.js +9 -0
- package/dist/react/index.js +5 -0
- package/dist/react/types.js +1 -0
- package/dist/react/useABTest.js +64 -0
- package/dist/react/useFeatureFlag.js +84 -0
- package/dist/react/useFeatureFlags.js +74 -0
- package/dist/react-native/FeatureFlagProvider.js +19 -0
- package/dist/react-native/identifyUser.js +9 -0
- package/dist/react-native/index.js +8 -0
- package/dist/react-native/offlineStorage.js +39 -0
- package/dist/react-native/types.js +1 -0
- package/dist/react-native/useABTest.js +87 -0
- package/dist/react-native/useFeatureFlag.js +82 -0
- package/dist/react-native/useFeatureFlags.js +115 -0
- package/dist/react-native.js +732 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +723 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/react.js +634 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +627 -0
- package/dist/react.mjs.map +1 -0
- package/dist/types/core/FlipFlagSDK.d.ts +94 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +1 -0
- package/dist/types/js/featureFlagManager.d.ts +59 -0
- package/dist/types/js/identifyUser.d.ts +11 -0
- package/dist/types/js/index.d.ts +4 -0
- package/dist/types/next/FlipflagProvider.d.ts +18 -0
- package/dist/types/next/identifyUser.d.ts +6 -0
- package/dist/types/next/index.d.ts +7 -0
- package/dist/types/next/server-utils.d.ts +15 -0
- package/dist/types/next/types.d.ts +24 -0
- package/dist/types/next/useABTest.d.ts +10 -0
- package/dist/types/next/useFeatureFlag.d.ts +10 -0
- package/dist/types/next/useFeatureFlags.d.ts +10 -0
- package/dist/types/react/FeatureFlagProvider.d.ts +14 -0
- package/dist/types/react/identifyUser.d.ts +6 -0
- package/dist/types/react/index.d.ts +6 -0
- package/dist/types/react/types.d.ts +20 -0
- package/dist/types/react/useABTest.d.ts +10 -0
- package/dist/types/react/useFeatureFlag.d.ts +10 -0
- package/dist/types/react/useFeatureFlags.d.ts +10 -0
- package/dist/types/react-native/FeatureFlagProvider.d.ts +14 -0
- package/dist/types/react-native/identifyUser.d.ts +6 -0
- package/dist/types/react-native/index.d.ts +9 -0
- package/dist/types/react-native/offlineStorage.d.ts +9 -0
- package/dist/types/react-native/types.d.ts +25 -0
- package/dist/types/react-native/useABTest.d.ts +10 -0
- package/dist/types/react-native/useFeatureFlag.d.ts +10 -0
- package/dist/types/react-native/useFeatureFlags.d.ts +10 -0
- package/dist/types/types/index.d.ts +86 -0
- package/dist/types/vue/identifyUser.d.ts +6 -0
- package/dist/types/vue/index.d.ts +8 -0
- package/dist/types/vue/plugin.d.ts +19 -0
- package/dist/types/vue/types.d.ts +20 -0
- package/dist/types/vue/useABTest.d.ts +10 -0
- package/dist/types/vue/useFeatureFlag.d.ts +10 -0
- package/dist/types/vue/useFeatureFlags.d.ts +10 -0
- package/dist/vue/identifyUser.js +13 -0
- package/dist/vue/index.js +7 -0
- package/dist/vue/plugin.js +30 -0
- package/dist/vue/types.js +1 -0
- package/dist/vue/useABTest.js +93 -0
- package/dist/vue/useFeatureFlag.js +87 -0
- package/dist/vue/useFeatureFlags.js +120 -0
- package/dist/vue.js +718 -0
- package/dist/vue.js.map +1 -0
- package/dist/vue.mjs +710 -0
- package/dist/vue.mjs.map +1 -0
- package/package.json +125 -0
package/README.md
ADDED
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
# FlipFlag SDK
|
|
2
|
+
|
|
3
|
+
A comprehensive, framework-agnostic SDK for feature flags and A/B testing with support for React, Vue, Next.js, React Native, and vanilla JavaScript.
|
|
4
|
+
|
|
5
|
+
## 🚀 Features
|
|
6
|
+
|
|
7
|
+
- **Multi-framework support**: React, Vue, Next.js, React Native, and vanilla JavaScript
|
|
8
|
+
- **A/B Testing**: Built-in support for experiment tracking and event recording
|
|
9
|
+
- **Caching**: Intelligent caching with configurable TTL
|
|
10
|
+
- **TypeScript**: Full TypeScript support with comprehensive type definitions
|
|
11
|
+
- **SSR Support**: Optimized for server-side rendering with Next.js
|
|
12
|
+
- **Retry Logic**: Automatic retry with exponential backoff
|
|
13
|
+
- **Rate Limiting**: Built-in handling of API rate limits
|
|
14
|
+
- **Offline Support**: Graceful fallbacks and offline capabilities
|
|
15
|
+
|
|
16
|
+
## 📦 Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install flipflag-sdk
|
|
20
|
+
# or
|
|
21
|
+
yarn add flipflag-sdk
|
|
22
|
+
# or
|
|
23
|
+
pnpm add flipflag-sdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 🔧 Configuration
|
|
27
|
+
|
|
28
|
+
First, create a configuration object:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { FlipFlagConfig } from "flipflag-sdk";
|
|
32
|
+
|
|
33
|
+
const config: FlipFlagConfig = {
|
|
34
|
+
apiKey: "ff_your_project_api_key",
|
|
35
|
+
projectId: "your-project-uuid",
|
|
36
|
+
environment: "production", // optional: 'dev', 'staging', 'prod'
|
|
37
|
+
userId: "user123", // optional: user identification for A/B testing
|
|
38
|
+
baseUrl: "https://app.flipflag.ru", // optional, defaults to production
|
|
39
|
+
pullInterval: 60000, // optional: auto-refresh interval in ms (60 seconds)
|
|
40
|
+
cacheTimeout: 30000, // optional, cache TTL in ms
|
|
41
|
+
retryAttempts: 3, // optional, number of retry attempts
|
|
42
|
+
retryDelay: 1000, // optional, base delay between retries in ms
|
|
43
|
+
};
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 🔄 Pull Interval Architecture
|
|
47
|
+
|
|
48
|
+
SDK работает на основе **pull интервала** - это значит, что:
|
|
49
|
+
|
|
50
|
+
1. **Автоматическое обновление**: SDK автоматически получает свежие данные каждые `pullInterval` миллисекунд
|
|
51
|
+
2. **Мгновенный доступ**: Хуки возвращают значения из локального состояния, без задержек
|
|
52
|
+
3. **Фоновая синхронизация**: Обновление происходит в фоне, не блокируя UI
|
|
53
|
+
4. **Идентификация пользователя**: A/B тесты и персонализированные флаги привязываются к `userId`
|
|
54
|
+
|
|
55
|
+
### Идентификация пользователя
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { identifyUser } from "flipflag-sdk/react";
|
|
59
|
+
|
|
60
|
+
// После авторизации пользователя
|
|
61
|
+
identifyUser({
|
|
62
|
+
userId: "user123",
|
|
63
|
+
userProperties: {
|
|
64
|
+
name: "John Doe",
|
|
65
|
+
email: "john@example.com",
|
|
66
|
+
plan: "premium",
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 🎯 React Usage
|
|
72
|
+
|
|
73
|
+
### Basic Setup
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import React from "react";
|
|
77
|
+
import { FeatureFlagProvider, useFeatureFlag } from "flipflag-sdk/react";
|
|
78
|
+
|
|
79
|
+
function App() {
|
|
80
|
+
return (
|
|
81
|
+
<FeatureFlagProvider config={config}>
|
|
82
|
+
<MyComponent />
|
|
83
|
+
</FeatureFlagProvider>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function MyComponent() {
|
|
88
|
+
const {
|
|
89
|
+
value: darkModeEnabled,
|
|
90
|
+
loading,
|
|
91
|
+
error,
|
|
92
|
+
} = useFeatureFlag("dark_mode");
|
|
93
|
+
|
|
94
|
+
if (loading) return <div>Loading...</div>;
|
|
95
|
+
if (error) return <div>Error: {error}</div>;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div className={darkModeEnabled ? "dark-theme" : "light-theme"}>
|
|
99
|
+
<h1>Welcome to FlipFlag!</h1>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Multiple Flags
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import { useFeatureFlags } from "flipflag-sdk/react";
|
|
109
|
+
|
|
110
|
+
function MyComponent() {
|
|
111
|
+
const { flags, loading } = useFeatureFlags([
|
|
112
|
+
"dark_mode",
|
|
113
|
+
"new_checkout",
|
|
114
|
+
"advanced_search",
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
if (loading) return <div>Loading...</div>;
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div>
|
|
121
|
+
{flags.new_checkout && <NewCheckoutFlow />}
|
|
122
|
+
{flags.advanced_search && <AdvancedSearch />}
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### A/B Testing
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { useABTest } from "flipflag-sdk/react";
|
|
132
|
+
|
|
133
|
+
function ButtonComponent() {
|
|
134
|
+
const {
|
|
135
|
+
variant: buttonColor,
|
|
136
|
+
loading,
|
|
137
|
+
recordEvent,
|
|
138
|
+
} = useABTest("button_color_test", {
|
|
139
|
+
userId: "user123",
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const handleClick = () => {
|
|
143
|
+
recordEvent("click", { page: "/home" });
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
if (loading) return <button>Loading...</button>;
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<button style={{ backgroundColor: buttonColor }} onClick={handleClick}>
|
|
150
|
+
Click me!
|
|
151
|
+
</button>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## ⚡ Next.js Usage
|
|
157
|
+
|
|
158
|
+
### App Router (with SSR)
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
// app/layout.tsx
|
|
162
|
+
import { FlipflagProvider } from "flipflag/next";
|
|
163
|
+
|
|
164
|
+
export default function RootLayout({
|
|
165
|
+
children,
|
|
166
|
+
serverFlags,
|
|
167
|
+
}: {
|
|
168
|
+
children: React.ReactNode;
|
|
169
|
+
serverFlags?: any;
|
|
170
|
+
}) {
|
|
171
|
+
return (
|
|
172
|
+
<html>
|
|
173
|
+
<body>
|
|
174
|
+
<FlipflagProvider config={config} serverFlags={serverFlags}>
|
|
175
|
+
{children}
|
|
176
|
+
</FlipflagProvider>
|
|
177
|
+
</body>
|
|
178
|
+
</html>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// app/page.tsx
|
|
183
|
+
import { getServerSideFlags } from "flipflag/next";
|
|
184
|
+
|
|
185
|
+
export default async function Page() {
|
|
186
|
+
const serverFlags = await getServerSideFlags(config, [
|
|
187
|
+
"dark_mode",
|
|
188
|
+
"new_feature",
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
return <RootLayout serverFlags={serverFlags}>...</RootLayout>;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Pages Router
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
// pages/_app.tsx
|
|
199
|
+
import { FlipflagProvider } from "flipflag/next";
|
|
200
|
+
|
|
201
|
+
function MyApp({ Component, pageProps }: AppProps) {
|
|
202
|
+
return (
|
|
203
|
+
<FlipflagProvider config={config} serverFlags={pageProps.serverFlags}>
|
|
204
|
+
<Component {...pageProps} />
|
|
205
|
+
</FlipflagProvider>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// pages/index.tsx
|
|
210
|
+
import { getServerSideFlags } from "flipflag/next";
|
|
211
|
+
|
|
212
|
+
export async function getServerSideProps() {
|
|
213
|
+
const serverFlags = await getServerSideFlags(config, ["dark_mode"]);
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
props: {
|
|
217
|
+
serverFlags,
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## 🟢 Vue Usage
|
|
224
|
+
|
|
225
|
+
### Basic Setup
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// main.ts
|
|
229
|
+
import { createApp } from "vue";
|
|
230
|
+
import { createFlipFlagPlugin } from "flipflag/vue";
|
|
231
|
+
import App from "./App.vue";
|
|
232
|
+
|
|
233
|
+
const config = {
|
|
234
|
+
apiKey: "ff_your_project_api_key",
|
|
235
|
+
projectId: "your-project-uuid",
|
|
236
|
+
environment: "production",
|
|
237
|
+
userId: "user123",
|
|
238
|
+
baseUrl: "https://app.flipflag.ru",
|
|
239
|
+
pullInterval: 60000, // Auto-refresh every 60 seconds
|
|
240
|
+
cacheTimeout: 30000,
|
|
241
|
+
retryAttempts: 3,
|
|
242
|
+
retryDelay: 1000,
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const app = createApp(App);
|
|
246
|
+
app.use(createFlipFlagPlugin(), config);
|
|
247
|
+
app.mount("#app");
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Using Hooks
|
|
251
|
+
|
|
252
|
+
```vue
|
|
253
|
+
<template>
|
|
254
|
+
<div>
|
|
255
|
+
<h1 v-if="darkMode">🌙 Dark Mode</h1>
|
|
256
|
+
<h1 v-else>☀️ Light Mode</h1>
|
|
257
|
+
|
|
258
|
+
<div v-for="feature in features" :key="feature.key">
|
|
259
|
+
<h3>{{ feature.name }}</h3>
|
|
260
|
+
<span :class="{ enabled: feature.enabled, disabled: !feature.enabled }">
|
|
261
|
+
{{ feature.enabled ? "✅ Enabled" : "❌ Disabled" }}
|
|
262
|
+
</span>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</template>
|
|
266
|
+
|
|
267
|
+
<script setup lang="ts">
|
|
268
|
+
import {
|
|
269
|
+
useFeatureFlag,
|
|
270
|
+
useFeatureFlags,
|
|
271
|
+
useABTest,
|
|
272
|
+
identifyUser,
|
|
273
|
+
} from "flipflag/vue";
|
|
274
|
+
|
|
275
|
+
const { value: darkMode } = useFeatureFlag("dark_mode");
|
|
276
|
+
|
|
277
|
+
const { flags } = useFeatureFlags([
|
|
278
|
+
"new_ui",
|
|
279
|
+
"advanced_search",
|
|
280
|
+
"social_features",
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
const { variant, variantId } = useABTest("cta_button_test", {
|
|
284
|
+
userId: "vue-user-123",
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Identify user
|
|
288
|
+
identifyUser({
|
|
289
|
+
userId: "vue-user-123",
|
|
290
|
+
userProperties: {
|
|
291
|
+
name: "Vue User",
|
|
292
|
+
version: "3.0",
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
</script>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## 📱 React Native Usage
|
|
299
|
+
|
|
300
|
+
### Basic Setup
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
// App.tsx
|
|
304
|
+
import React from "react";
|
|
305
|
+
import { FeatureFlagProvider } from "flipflag/react-native";
|
|
306
|
+
import { useFeatureFlag, useABTest, identifyUser } from "flipflag/react-native";
|
|
307
|
+
|
|
308
|
+
const config = {
|
|
309
|
+
apiKey: "ff_your_project_api_key",
|
|
310
|
+
projectId: "your-project-uuid",
|
|
311
|
+
environment: "production",
|
|
312
|
+
userId: "rn-user-123",
|
|
313
|
+
baseUrl: "https://app.flipflag.ru",
|
|
314
|
+
pullInterval: 60000,
|
|
315
|
+
cacheTimeout: 30000,
|
|
316
|
+
retryAttempts: 3,
|
|
317
|
+
retryDelay: 1000,
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
function App() {
|
|
321
|
+
return (
|
|
322
|
+
<FeatureFlagProvider config={config}>
|
|
323
|
+
<MyComponent />
|
|
324
|
+
</FeatureFlagProvider>
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Using Hooks
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
import React from "react";
|
|
333
|
+
import { View, Text, TouchableOpacity } from "react-native";
|
|
334
|
+
import {
|
|
335
|
+
useFeatureFlag,
|
|
336
|
+
useFeatureFlags,
|
|
337
|
+
useABTest,
|
|
338
|
+
identifyUser,
|
|
339
|
+
} from "flipflag/react-native";
|
|
340
|
+
|
|
341
|
+
function MyComponent() {
|
|
342
|
+
const { value: darkMode, loading } = useFeatureFlag("dark_mode");
|
|
343
|
+
|
|
344
|
+
const { flags } = useFeatureFlags([
|
|
345
|
+
"new_ui",
|
|
346
|
+
"offline_mode",
|
|
347
|
+
"push_notifications",
|
|
348
|
+
]);
|
|
349
|
+
|
|
350
|
+
const { variant, variantId, recordEvent } = useABTest("cta_button_test", {
|
|
351
|
+
userId: "rn-user-123",
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const handleIdentifyUser = () => {
|
|
355
|
+
identifyUser({
|
|
356
|
+
userId: "rn-user-123",
|
|
357
|
+
userProperties: {
|
|
358
|
+
platform: "iOS",
|
|
359
|
+
version: "1.0.0",
|
|
360
|
+
device: "iPhone",
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const handleButtonPress = () => {
|
|
366
|
+
recordEvent("click", {
|
|
367
|
+
screen: "home",
|
|
368
|
+
element: "cta_button",
|
|
369
|
+
});
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
if (loading) {
|
|
373
|
+
return <Text>Loading...</Text>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<View style={{ backgroundColor: darkMode ? "#333" : "#fff" }}>
|
|
378
|
+
<Text style={{ color: darkMode ? "#fff" : "#333" }}>
|
|
379
|
+
{darkMode ? "🌙 Dark Mode" : "☀️ Light Mode"}
|
|
380
|
+
</Text>
|
|
381
|
+
|
|
382
|
+
<TouchableOpacity onPress={handleIdentifyUser}>
|
|
383
|
+
<Text>👤 Identify User</Text>
|
|
384
|
+
</TouchableOpacity>
|
|
385
|
+
|
|
386
|
+
<TouchableOpacity
|
|
387
|
+
onPress={handleButtonPress}
|
|
388
|
+
style={{ backgroundColor: variant || "#007bff" }}
|
|
389
|
+
>
|
|
390
|
+
<Text>Test Button (Variant: {variantId})</Text>
|
|
391
|
+
</TouchableOpacity>
|
|
392
|
+
</View>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## 🌐 Vanilla JavaScript Usage
|
|
398
|
+
|
|
399
|
+
```javascript
|
|
400
|
+
import { createFeatureFlagManager } from "flipflag/js";
|
|
401
|
+
|
|
402
|
+
const flagManager = createFeatureFlagManager({
|
|
403
|
+
config,
|
|
404
|
+
autoRefresh: true,
|
|
405
|
+
refreshInterval: 30000, // 30 seconds
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// Get a single flag
|
|
409
|
+
const darkModeEnabled = await flagManager.getFlag("dark_mode");
|
|
410
|
+
|
|
411
|
+
// Get multiple flags
|
|
412
|
+
const flags = await flagManager.getFlags(["dark_mode", "new_checkout"]);
|
|
413
|
+
|
|
414
|
+
// Subscribe to flag changes
|
|
415
|
+
const unsubscribe = flagManager.onFlagChange("dark_mode", (value) => {
|
|
416
|
+
console.log("Dark mode changed to:", value);
|
|
417
|
+
updateTheme(value);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// A/B Testing
|
|
421
|
+
const { variant, variantId } = await flagManager.getABTestVariant(
|
|
422
|
+
"button_test",
|
|
423
|
+
"user123"
|
|
424
|
+
);
|
|
425
|
+
await flagManager.recordABTestEvent("button_test", "user123", "click", {
|
|
426
|
+
page: "/home",
|
|
427
|
+
element: "hero_button",
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// Cleanup
|
|
431
|
+
unsubscribe();
|
|
432
|
+
flagManager.stopAutoRefresh();
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## 🎨 Advanced Usage
|
|
436
|
+
|
|
437
|
+
### Custom Cache Strategy
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
import { FlipFlagSDK } from "flipflag";
|
|
441
|
+
|
|
442
|
+
const sdk = new FlipFlagSDK({
|
|
443
|
+
...config,
|
|
444
|
+
cacheTimeout: 60000, // 1 minute cache
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Manual cache management
|
|
448
|
+
sdk.clearCache(); // Clear all cache
|
|
449
|
+
sdk.clearCache("flags:project123:prod"); // Clear specific cache entry
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Error Handling and Fallbacks
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
const { value: featureEnabled, error } = useFeatureFlag(
|
|
456
|
+
"experimental_feature",
|
|
457
|
+
{
|
|
458
|
+
fallbackValue: false, // Use false if API fails
|
|
459
|
+
}
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
if (error) {
|
|
463
|
+
console.warn("Feature flag error:", error);
|
|
464
|
+
// Continue with fallback behavior
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Retry Configuration
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
const sdk = new FlipFlagSDK({
|
|
472
|
+
...config,
|
|
473
|
+
retryAttempts: 5,
|
|
474
|
+
retryDelay: 2000, // 2 second base delay
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## 🔄 A/B Testing Events
|
|
479
|
+
|
|
480
|
+
The SDK supports tracking various events for your A/B tests:
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
// Track different types of events
|
|
484
|
+
recordEvent("view"); // User viewed the variant
|
|
485
|
+
recordEvent("click", { element: "button" }); // User clicked something
|
|
486
|
+
recordEvent("conversion", { revenue: 29.99 }); // User completed a conversion
|
|
487
|
+
recordEvent("custom_event", { customData: "value" }); // Custom event
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## 📱 Framework-Specific Features
|
|
491
|
+
|
|
492
|
+
### React
|
|
493
|
+
|
|
494
|
+
- ✅ Hooks: `useFeatureFlag`, `useFeatureFlags`, `useABTest`
|
|
495
|
+
- ✅ Context provider for state management
|
|
496
|
+
- ✅ Automatic re-renders on flag changes
|
|
497
|
+
- ✅ TypeScript support
|
|
498
|
+
|
|
499
|
+
### Next.js
|
|
500
|
+
|
|
501
|
+
- ✅ SSR/SSG support with `getServerSideFlags`
|
|
502
|
+
- ✅ Hydration-safe flag values
|
|
503
|
+
- ✅ Static generation with `getStaticPropsWithFlags`
|
|
504
|
+
- ✅ App Router and Pages Router support
|
|
505
|
+
|
|
506
|
+
### Vue
|
|
507
|
+
|
|
508
|
+
- ✅ Composition API hooks (`useFeatureFlag`, `useFeatureFlags`, `useABTest`)
|
|
509
|
+
- ✅ Vue plugin for global state management
|
|
510
|
+
- ✅ Reactive flag values with automatic updates
|
|
511
|
+
- ✅ Full TypeScript support
|
|
512
|
+
- ✅ Plugin-based dependency injection
|
|
513
|
+
|
|
514
|
+
### React Native
|
|
515
|
+
|
|
516
|
+
- ✅ React hooks (`useFeatureFlag`, `useFeatureFlags`, `useABTest`)
|
|
517
|
+
- ✅ Offline storage with AsyncStorage
|
|
518
|
+
- ✅ Provider pattern for state management
|
|
519
|
+
- ✅ Network-aware caching
|
|
520
|
+
- ✅ Native performance optimizations
|
|
521
|
+
|
|
522
|
+
### Vanilla JavaScript
|
|
523
|
+
|
|
524
|
+
- ✅ Feature flag manager with event subscriptions
|
|
525
|
+
- ✅ Auto-refresh capabilities
|
|
526
|
+
- ✅ Memory-efficient caching
|
|
527
|
+
- ✅ Framework-agnostic API
|
|
528
|
+
|
|
529
|
+
## 🔒 Security Best Practices
|
|
530
|
+
|
|
531
|
+
1. **API Key Storage**: Never expose API keys in client-side code
|
|
532
|
+
2. **Environment Variables**: Use environment variables for configuration
|
|
533
|
+
3. **Rate Limiting**: SDK handles rate limiting automatically
|
|
534
|
+
4. **Error Handling**: Always provide fallback values for critical features
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
// Secure configuration example
|
|
538
|
+
const config = {
|
|
539
|
+
apiKey: process.env.FLIPFLAG_API_KEY!,
|
|
540
|
+
projectId: process.env.FLIPFLAG_PROJECT_ID!,
|
|
541
|
+
environment: process.env.NODE_ENV === "production" ? "prod" : "dev",
|
|
542
|
+
};
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
## 🚀 Production Optimization
|
|
546
|
+
|
|
547
|
+
### Caching Strategy
|
|
548
|
+
|
|
549
|
+
```typescript
|
|
550
|
+
const config = {
|
|
551
|
+
...baseConfig,
|
|
552
|
+
cacheTimeout: 300000, // 5 minutes for production
|
|
553
|
+
};
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### Bundle Splitting
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
// Only import what you need
|
|
560
|
+
import { useFeatureFlag } from "flipflag/react"; // Tree-shakable
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### CDN Deployment
|
|
564
|
+
|
|
565
|
+
Host the SDK on a CDN for faster loading:
|
|
566
|
+
|
|
567
|
+
```html
|
|
568
|
+
<script src="https://cdn.example.com/flipflag.js"></script>
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
## 📊 Monitoring and Analytics
|
|
572
|
+
|
|
573
|
+
The SDK provides built-in metrics collection:
|
|
574
|
+
|
|
575
|
+
- Request success/failure rates
|
|
576
|
+
- Cache hit rates
|
|
577
|
+
- A/B test conversion tracking
|
|
578
|
+
- Performance metrics
|
|
579
|
+
|
|
580
|
+
## 🛠️ Development
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
# Install dependencies
|
|
584
|
+
npm install
|
|
585
|
+
|
|
586
|
+
# Build the project
|
|
587
|
+
npm run build
|
|
588
|
+
|
|
589
|
+
# Run tests
|
|
590
|
+
npm test
|
|
591
|
+
|
|
592
|
+
# Development mode with watch
|
|
593
|
+
npm run dev
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
## 🎮 Demo Projects
|
|
597
|
+
|
|
598
|
+
Протестируйте SDK в действии! В папке `demo/` находятся полнофункциональные примеры:
|
|
599
|
+
|
|
600
|
+
### Быстрый запуск всех демо
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
cd demo
|
|
604
|
+
./start-all.sh start
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
### Индивидуальный запуск
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
# React demo
|
|
611
|
+
cd demo/react-demo && npm install && npm link flipflag && npm start
|
|
612
|
+
|
|
613
|
+
# Next.js demo
|
|
614
|
+
cd demo/next-demo && npm install && npm link flipflag && npm run dev
|
|
615
|
+
|
|
616
|
+
# Vue demo
|
|
617
|
+
cd demo/vue-demo && npm install && npm link flipflag && npm run dev
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### Что демонстрируют демо-проекты
|
|
621
|
+
|
|
622
|
+
- ✅ **Feature Flags** - Управление видимостью функций
|
|
623
|
+
- ✅ **A/B Testing** - Тестирование различных вариантов
|
|
624
|
+
- ✅ **Caching** - Интеллектуальное кэширование
|
|
625
|
+
- ✅ **Error Handling** - Обработка ошибок и fallback значения
|
|
626
|
+
- ✅ **Real-time Updates** - Обновление флагов без перезагрузки
|
|
627
|
+
|
|
628
|
+
## 📦 Publishing Guide
|
|
629
|
+
|
|
630
|
+
Готовы опубликовать пакет на npm? Следуйте подробному руководству:
|
|
631
|
+
|
|
632
|
+
👉 **[PUBLISH_GUIDE.md](./PUBLISH_GUIDE.md)** - Полное руководство по публикации
|
|
633
|
+
|
|
634
|
+
### Краткая версия
|
|
635
|
+
|
|
636
|
+
```bash
|
|
637
|
+
# Подготовка
|
|
638
|
+
npm run build
|
|
639
|
+
npm login
|
|
640
|
+
|
|
641
|
+
# Публикация
|
|
642
|
+
npm publish
|
|
643
|
+
|
|
644
|
+
# Проверка
|
|
645
|
+
npm view flipflag
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
## 📚 API Reference
|
|
649
|
+
|
|
650
|
+
### Core SDK Methods
|
|
651
|
+
|
|
652
|
+
#### `getFlags(projectId?, environment?)`
|
|
653
|
+
|
|
654
|
+
Get all feature flags for a project.
|
|
655
|
+
|
|
656
|
+
#### `getFlag(flagName, projectId?, environment?)`
|
|
657
|
+
|
|
658
|
+
Get a specific feature flag value.
|
|
659
|
+
|
|
660
|
+
#### `getAbTestVariant(testName, userId, projectId?)`
|
|
661
|
+
|
|
662
|
+
Get A/B test variant for a user.
|
|
663
|
+
|
|
664
|
+
#### `recordAbTestEvent(testName, userId, event, variantId, eventData?)`
|
|
665
|
+
|
|
666
|
+
Record an A/B test event.
|
|
667
|
+
|
|
668
|
+
### React Hooks
|
|
669
|
+
|
|
670
|
+
#### `useFeatureFlag(flagName, options?)`
|
|
671
|
+
|
|
672
|
+
React hook for single feature flag.
|
|
673
|
+
|
|
674
|
+
#### `useFeatureFlags(flagNames, options?)`
|
|
675
|
+
|
|
676
|
+
React hook for multiple feature flags.
|
|
677
|
+
|
|
678
|
+
#### `useABTest(testName, options)`
|
|
679
|
+
|
|
680
|
+
React hook for A/B testing.
|
|
681
|
+
|
|
682
|
+
### Configuration Options
|
|
683
|
+
|
|
684
|
+
```typescript
|
|
685
|
+
interface FlipFlagConfig {
|
|
686
|
+
apiKey: string; // Required: Your API key
|
|
687
|
+
baseUrl?: string; // Optional: API base URL
|
|
688
|
+
projectId: string; // Required: Project UUID
|
|
689
|
+
environment?: string; // Optional: 'dev', 'staging', 'prod'
|
|
690
|
+
cacheTimeout?: number; // Optional: Cache TTL in ms
|
|
691
|
+
retryAttempts?: number; // Optional: Number of retry attempts
|
|
692
|
+
retryDelay?: number; // Optional: Base retry delay in ms
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
## 🤝 Contributing
|
|
697
|
+
|
|
698
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
699
|
+
|
|
700
|
+
## 📄 License
|
|
701
|
+
|
|
702
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
703
|
+
|
|
704
|
+
## 🆘 Support
|
|
705
|
+
|
|
706
|
+
- 📖 [Documentation](https://docs.flipflag.ru)
|
|
707
|
+
- 💬 [Discord Community](https://discord.gg/flipflag)
|
|
708
|
+
- 🐛 [Issue Tracker](https://github.com/flipflag/flipflag-sdk/issues)
|
|
709
|
+
- 📧 [Email Support](mailto:support@flipflag.ru)
|
|
710
|
+
|
|
711
|
+
---
|
|
712
|
+
|
|
713
|
+
Made with ❤️ by the FlipFlag team
|