@flipflag/sdk 1.3.0 → 1.5.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 +536 -39
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/browser.min.js +1 -1
- package/dist/{flipflag-core-v7AUprKd.d.ts → flipflag-core-3rbGd7Gt.d.ts} +8 -1
- package/dist/flipflag-core-D9bYmvZd.js +2 -0
- package/dist/flipflag-core-D9bYmvZd.js.map +1 -0
- package/dist/{flipflag-core-BNqlFhCW.js → flipflag-core-DzEU3Qfu.js} +19 -6
- package/dist/flipflag-core-DzEU3Qfu.js.map +1 -0
- package/dist/node.d.ts +1 -1
- package/dist/node.js +1 -1
- package/dist/node.min.js +1 -1
- package/package.json +1 -1
- package/dist/flipflag-core-BNqlFhCW.js.map +0 -1
- package/dist/flipflag-core-DHCsP2TB.js +0 -2
- package/dist/flipflag-core-DHCsP2TB.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,37 +1,26 @@
|
|
|
1
1
|
# FlipFlag SDK
|
|
2
2
|
|
|
3
|
-
A lightweight client-side SDK for
|
|
3
|
+
A lightweight, feature-rich client-side SDK for **FlipFlag** (https://flipflag.dev) — a powerful feature flag management platform.
|
|
4
4
|
|
|
5
|
-
The SDK is designed to be simple, declarative, and safe by default.
|
|
6
|
-
It supports read-only usage as well as full feature management when a private key is provided.
|
|
5
|
+
The SDK is designed to be simple, declarative, and safe by default. It supports both browser and Node.js environments with automatic feature flag synchronization and usage tracking.
|
|
7
6
|
|
|
8
7
|
---
|
|
9
8
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
- Load feature flags (`enabled`) using a **public key**
|
|
13
|
-
- Declare feature activation windows via `.flipflag.yml`
|
|
14
|
-
- Automatically create features on the server (requires `privateKey`)
|
|
15
|
-
- Periodically sync:
|
|
16
|
-
- feature flags
|
|
17
|
-
- feature timing declarations
|
|
18
|
-
- feature usage events
|
|
19
|
-
- Local in-memory cache for flags, declarations, and usage
|
|
20
|
-
- Full TypeScript support
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Installation
|
|
9
|
+
## 📦 Installation
|
|
25
10
|
|
|
26
11
|
```sh
|
|
27
12
|
npm install @flipflag/sdk
|
|
28
13
|
# or
|
|
29
14
|
yarn add @flipflag/sdk
|
|
15
|
+
# or
|
|
16
|
+
pnpm add @flipflag/sdk
|
|
30
17
|
```
|
|
31
18
|
|
|
32
19
|
---
|
|
33
20
|
|
|
34
|
-
## Quick Start
|
|
21
|
+
## 🚀 Quick Start
|
|
22
|
+
|
|
23
|
+
### Node.js (with YAML config)
|
|
35
24
|
|
|
36
25
|
```ts
|
|
37
26
|
import { FlipFlag } from "@flipflag/sdk";
|
|
@@ -48,16 +37,234 @@ if (manager.isEnabled("newFeature")) {
|
|
|
48
37
|
}
|
|
49
38
|
```
|
|
50
39
|
|
|
40
|
+
### Browser (with inline config)
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { FlipFlag } from "@flipflag/sdk";
|
|
44
|
+
|
|
45
|
+
const manager = new FlipFlag(
|
|
46
|
+
{
|
|
47
|
+
publicKey: "YOUR_PUBLIC_KEY",
|
|
48
|
+
privateKey: "YOUR_PRIVATE_KEY",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
// Initial configuration
|
|
52
|
+
newFeature: {
|
|
53
|
+
contributor: "dev@example.com",
|
|
54
|
+
times: [
|
|
55
|
+
{
|
|
56
|
+
started: "2025-12-01T00:00:00.000Z",
|
|
57
|
+
finished: "2025-12-31T23:59:59.000Z",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
await manager.init();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 🎯 Features
|
|
70
|
+
|
|
71
|
+
- ✅ **Dual environment support**: Node.js and Browser
|
|
72
|
+
- ✅ **Read-only mode**: Use only `publicKey` to fetch flags
|
|
73
|
+
- ✅ **Full management**: Use `privateKey` to declare features and track usage
|
|
74
|
+
- ✅ **YAML configuration**: Declarative feature definitions (Node.js)
|
|
75
|
+
- ✅ **Programmatic API**: Declare features via code (Browser)
|
|
76
|
+
- ✅ **Auto-sync**: Periodic polling for flags and statistics
|
|
77
|
+
- ✅ **Usage tracking**: Automatic feature usage analytics
|
|
78
|
+
- ✅ **Time windows**: Define feature activation periods
|
|
79
|
+
- ✅ **TypeScript**: Full type safety out of the box
|
|
80
|
+
- ✅ **Lightweight**: Minimal dependencies
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 📚 Configuration Options
|
|
85
|
+
|
|
86
|
+
### `IManagerOptions`
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
interface IManagerOptions {
|
|
90
|
+
/**
|
|
91
|
+
* FlipFlag API base URL.
|
|
92
|
+
* @default "https://api.flipflag.dev"
|
|
93
|
+
*/
|
|
94
|
+
apiUrl?: string;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Public key for fetching feature flags (required).
|
|
98
|
+
* Get it from your FlipFlag project settings.
|
|
99
|
+
*/
|
|
100
|
+
publicKey: string;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Private key for declaring features and syncing usage (optional).
|
|
104
|
+
* Without it, SDK works in read-only mode.
|
|
105
|
+
*/
|
|
106
|
+
privateKey?: string;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Path to .flipflag.yml config file (Node.js only).
|
|
110
|
+
* @default "<process.cwd()>/.flipflag.yml"
|
|
111
|
+
*/
|
|
112
|
+
configPath?: string;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* If true, missing config file won't throw an error.
|
|
116
|
+
* @default true
|
|
117
|
+
*/
|
|
118
|
+
ignoreMissingConfig?: boolean;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Polling interval for fetching feature flags (milliseconds).
|
|
122
|
+
* @default 30000 (30 seconds)
|
|
123
|
+
*/
|
|
124
|
+
pollingInterval?: number;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Sync interval for sending declarations and usage stats (milliseconds).
|
|
128
|
+
* @default 90000 (90 seconds)
|
|
129
|
+
*/
|
|
130
|
+
syncInterval?: number;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
51
134
|
---
|
|
52
135
|
|
|
53
|
-
##
|
|
136
|
+
## 📖 Usage Examples
|
|
137
|
+
|
|
138
|
+
### Read-Only Mode (Public Key Only)
|
|
139
|
+
|
|
140
|
+
Perfect for client-side applications where you only need to check feature states:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const manager = new FlipFlag({
|
|
144
|
+
publicKey: "pub_xxxxxxxxxxxxx",
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
await manager.init();
|
|
54
148
|
|
|
55
|
-
|
|
56
|
-
(
|
|
149
|
+
// Check feature state
|
|
150
|
+
if (manager.isEnabled("darkMode")) {
|
|
151
|
+
enableDarkMode();
|
|
152
|
+
}
|
|
57
153
|
|
|
58
|
-
|
|
154
|
+
// Cleanup when done
|
|
155
|
+
manager.destroy();
|
|
156
|
+
```
|
|
59
157
|
|
|
60
|
-
|
|
158
|
+
### Full Mode (with Private Key)
|
|
159
|
+
|
|
160
|
+
For applications that manage features and track usage:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
const manager = new FlipFlag({
|
|
164
|
+
publicKey: "pub_xxxxxxxxxxxxx",
|
|
165
|
+
privateKey: "priv_xxxxxxxxxxxxx",
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
await manager.init();
|
|
169
|
+
|
|
170
|
+
// Features will be automatically declared from .flipflag.yml
|
|
171
|
+
// and usage will be tracked
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Custom Intervals
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
const manager = new FlipFlag({
|
|
178
|
+
publicKey: "pub_xxxxxxxxxxxxx",
|
|
179
|
+
pollingInterval: 10_000, // Check flags every 10 seconds
|
|
180
|
+
syncInterval: 60_000, // Sync usage every 60 seconds
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Manual Sync
|
|
185
|
+
|
|
186
|
+
Force immediate synchronization without waiting for intervals:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
await manager.init();
|
|
190
|
+
|
|
191
|
+
// ... some time later
|
|
192
|
+
await manager.sync(); // Syncs declarations and usage immediately
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Browser Usage with Programmatic Configuration
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { FlipFlag } from "@flipflag/sdk";
|
|
199
|
+
|
|
200
|
+
const manager = new FlipFlag(
|
|
201
|
+
{
|
|
202
|
+
publicKey: "pub_xxxxxxxxxxxxx",
|
|
203
|
+
privateKey: "priv_xxxxxxxxxxxxx",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
newPaymentFlow: {
|
|
207
|
+
description: "New Stripe integration",
|
|
208
|
+
contributor: "payments-team@example.com",
|
|
209
|
+
type: "feature",
|
|
210
|
+
times: [
|
|
211
|
+
{
|
|
212
|
+
started: "2025-01-15T00:00:00.000Z",
|
|
213
|
+
finished: null, // No end date
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
legacyCheckout: {
|
|
218
|
+
contributor: "payments-team@example.com",
|
|
219
|
+
times: [
|
|
220
|
+
{
|
|
221
|
+
started: "2024-01-01T00:00:00.000Z",
|
|
222
|
+
finished: "2025-03-01T00:00:00.000Z", // Will be disabled after this date
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
await manager.init();
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Dynamic Feature Declaration
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
const manager = new FlipFlag({
|
|
236
|
+
publicKey: "pub_xxxxxxxxxxxxx",
|
|
237
|
+
privateKey: "priv_xxxxxxxxxxxxx",
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await manager.init();
|
|
241
|
+
|
|
242
|
+
// Add features programmatically
|
|
243
|
+
manager.declareFromObject({
|
|
244
|
+
experimentalUI: {
|
|
245
|
+
contributor: "ui-team@example.com",
|
|
246
|
+
times: [
|
|
247
|
+
{
|
|
248
|
+
started: new Date().toISOString(),
|
|
249
|
+
finished: null,
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Sync immediately
|
|
256
|
+
await manager.sync();
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 📝 YAML Configuration (Node.js)
|
|
262
|
+
|
|
263
|
+
The SDK automatically loads `.flipflag.yml` from your project root during `init()`.
|
|
264
|
+
|
|
265
|
+
### Basic Example
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
61
268
|
newFeature:
|
|
62
269
|
contributor: epolevov@emd.one
|
|
63
270
|
times:
|
|
@@ -68,23 +275,113 @@ anotherFeature:
|
|
|
68
275
|
contributor: dev@company.com
|
|
69
276
|
times:
|
|
70
277
|
- started: "2026-01-01T00:00:00.000Z"
|
|
278
|
+
finished: null
|
|
71
279
|
```
|
|
72
280
|
|
|
73
|
-
|
|
281
|
+
### Advanced Example with Multiple Time Windows
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
seasonalFeature:
|
|
285
|
+
description: "Holiday sale banner"
|
|
286
|
+
contributor: marketing@example.com
|
|
287
|
+
type: "feature"
|
|
288
|
+
times:
|
|
289
|
+
# Black Friday 2025
|
|
290
|
+
- started: "2025-11-24T00:00:00.000Z"
|
|
291
|
+
finished: "2025-11-30T23:59:59.000Z"
|
|
292
|
+
# Christmas 2025
|
|
293
|
+
- started: "2025-12-20T00:00:00.000Z"
|
|
294
|
+
finished: "2025-12-26T23:59:59.000Z"
|
|
295
|
+
|
|
296
|
+
betaFeature:
|
|
297
|
+
description: "New analytics dashboard"
|
|
298
|
+
contributor: analytics-team@example.com
|
|
299
|
+
type: "experiment"
|
|
300
|
+
times:
|
|
301
|
+
- started: "2025-01-01T00:00:00.000Z"
|
|
302
|
+
finished: null # No end date - always active
|
|
303
|
+
```
|
|
74
304
|
|
|
75
|
-
|
|
305
|
+
### Custom Config Path
|
|
76
306
|
|
|
77
307
|
```ts
|
|
78
|
-
manager
|
|
308
|
+
const manager = new FlipFlag({
|
|
309
|
+
publicKey: "pub_xxxxxxxxxxxxx",
|
|
310
|
+
configPath: "./config/features.yml",
|
|
311
|
+
});
|
|
79
312
|
```
|
|
80
313
|
|
|
81
314
|
---
|
|
82
315
|
|
|
83
|
-
##
|
|
316
|
+
## 🔌 API Reference
|
|
317
|
+
|
|
318
|
+
### `FlipFlag` Class
|
|
319
|
+
|
|
320
|
+
#### Constructor
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
// Node.js
|
|
324
|
+
new FlipFlag(options: IManagerOptions)
|
|
325
|
+
|
|
326
|
+
// Browser
|
|
327
|
+
new FlipFlag(options: IManagerOptions, initialConfig?: FlipFlagYaml)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### Methods
|
|
331
|
+
|
|
332
|
+
##### `init(): Promise<void>`
|
|
333
|
+
|
|
334
|
+
Initializes the SDK:
|
|
335
|
+
- Loads YAML config (Node.js only)
|
|
336
|
+
- Fetches current feature flags from server
|
|
337
|
+
- Syncs declared features
|
|
338
|
+
- Starts automatic polling and sync intervals
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
await manager.init();
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
##### `isEnabled(featureName: string): boolean`
|
|
345
|
+
|
|
346
|
+
Checks if a feature is enabled. Also:
|
|
347
|
+
- Tracks usage automatically
|
|
348
|
+
- Creates feature on server if it doesn't exist (requires `privateKey`)
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
if (manager.isEnabled("darkMode")) {
|
|
352
|
+
// Feature is enabled
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
##### `declareFromObject(config: FlipFlagYaml): void`
|
|
357
|
+
|
|
358
|
+
Programmatically declares features without YAML file.
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
manager.declareFromObject({
|
|
362
|
+
myFeature: {
|
|
363
|
+
contributor: "dev@example.com",
|
|
364
|
+
times: [{ started: "2025-01-01T00:00:00.000Z", finished: null }],
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
##### `sync(): Promise<void>`
|
|
370
|
+
|
|
371
|
+
Manually triggers synchronization:
|
|
372
|
+
- Syncs feature declarations
|
|
373
|
+
- Sends usage statistics
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
await manager.sync();
|
|
377
|
+
```
|
|
84
378
|
|
|
85
|
-
|
|
379
|
+
##### `destroy(): void`
|
|
86
380
|
|
|
87
|
-
|
|
381
|
+
Stops all timers and clears local state:
|
|
382
|
+
- Stops polling interval
|
|
383
|
+
- Stops sync interval
|
|
384
|
+
- Clears cached flags and usage data
|
|
88
385
|
|
|
89
386
|
```ts
|
|
90
387
|
manager.destroy();
|
|
@@ -92,20 +389,220 @@ manager.destroy();
|
|
|
92
389
|
|
|
93
390
|
---
|
|
94
391
|
|
|
95
|
-
##
|
|
392
|
+
## 🎨 TypeScript Types
|
|
393
|
+
|
|
394
|
+
### `FlipFlagYaml`
|
|
96
395
|
|
|
97
396
|
```ts
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
397
|
+
type FlipFlagYaml = Record<string, YamlFeature>;
|
|
398
|
+
|
|
399
|
+
interface YamlFeature {
|
|
400
|
+
description?: string;
|
|
401
|
+
contributor?: string;
|
|
402
|
+
type?: string;
|
|
403
|
+
times?: YamlTime[];
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
interface YamlTime {
|
|
407
|
+
started: string; // ISO 8601 date string
|
|
408
|
+
finished: string | null; // ISO 8601 date string or null for no end
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### `IDeclareFeatureTime`
|
|
413
|
+
|
|
414
|
+
```ts
|
|
415
|
+
interface IDeclareFeatureTime {
|
|
416
|
+
email: string;
|
|
417
|
+
start: string; // ISO 8601 date string
|
|
418
|
+
end?: string; // ISO 8601 date string
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### `IFeatureFlag`
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
interface IFeatureFlag {
|
|
426
|
+
enabled: boolean;
|
|
104
427
|
}
|
|
105
428
|
```
|
|
106
429
|
|
|
107
430
|
---
|
|
108
431
|
|
|
109
|
-
##
|
|
432
|
+
## 💡 Best Practices
|
|
433
|
+
|
|
434
|
+
### 1. Use Environment Variables
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
const manager = new FlipFlag({
|
|
438
|
+
publicKey: process.env.FLIPFLAG_PUBLIC_KEY!,
|
|
439
|
+
privateKey: process.env.FLIPFLAG_PRIVATE_KEY,
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### 2. Initialize Early
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
// app.ts
|
|
447
|
+
const manager = new FlipFlag({ /* ... */ });
|
|
448
|
+
await manager.init();
|
|
449
|
+
|
|
450
|
+
// Now use throughout your app
|
|
451
|
+
export { manager };
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### 3. Cleanup on Shutdown
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
process.on("SIGTERM", () => {
|
|
458
|
+
manager.destroy();
|
|
459
|
+
process.exit(0);
|
|
460
|
+
});
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### 4. Use Read-Only Mode in Production Frontend
|
|
464
|
+
|
|
465
|
+
For security, only use `publicKey` in client-side production code:
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
// ✅ Good - read-only
|
|
469
|
+
const manager = new FlipFlag({
|
|
470
|
+
publicKey: import.meta.env.VITE_FLIPFLAG_PUBLIC_KEY,
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// ❌ Bad - exposes private key
|
|
474
|
+
const manager = new FlipFlag({
|
|
475
|
+
publicKey: import.meta.env.VITE_FLIPFLAG_PUBLIC_KEY,
|
|
476
|
+
privateKey: import.meta.env.VITE_FLIPFLAG_PRIVATE_KEY, // Don't do this!
|
|
477
|
+
});
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### 5. Handle Initialization Errors
|
|
481
|
+
|
|
482
|
+
```ts
|
|
483
|
+
try {
|
|
484
|
+
await manager.init();
|
|
485
|
+
} catch (error) {
|
|
486
|
+
console.error("Failed to initialize FlipFlag:", error);
|
|
487
|
+
// Fallback to default behavior
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### 6. Use Feature Flags Defensively
|
|
492
|
+
|
|
493
|
+
```ts
|
|
494
|
+
// Default to false if something goes wrong
|
|
495
|
+
const isEnabled = manager?.isEnabled("newFeature") ?? false;
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## 🔧 Troubleshooting
|
|
501
|
+
|
|
502
|
+
### Config file not found
|
|
503
|
+
|
|
504
|
+
```
|
|
505
|
+
Error: FlipFlag: cannot read config at /path/to/.flipflag.yml: ENOENT
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**Solution**: Set `ignoreMissingConfig: true` or create the config file:
|
|
509
|
+
|
|
510
|
+
```ts
|
|
511
|
+
const manager = new FlipFlag({
|
|
512
|
+
publicKey: "pub_xxxxxxxxxxxxx",
|
|
513
|
+
ignoreMissingConfig: true,
|
|
514
|
+
});
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Invalid date format
|
|
518
|
+
|
|
519
|
+
```
|
|
520
|
+
Error: FlipFlag: invalid "started" date in myFeature: not-a-date
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Solution**: Use ISO 8601 format for dates:
|
|
524
|
+
|
|
525
|
+
```yaml
|
|
526
|
+
myFeature:
|
|
527
|
+
times:
|
|
528
|
+
- started: "2025-01-01T00:00:00.000Z" # ✅ Correct
|
|
529
|
+
# - started: "2025-01-01" # ❌ Wrong
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Public key missing
|
|
533
|
+
|
|
534
|
+
```
|
|
535
|
+
Error: Public key is missing. Please provide a valid publicKey in the SDK configuration.
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**Solution**: Always provide a `publicKey`:
|
|
539
|
+
|
|
540
|
+
```ts
|
|
541
|
+
const manager = new FlipFlag({
|
|
542
|
+
publicKey: "pub_xxxxxxxxxxxxx", // Required!
|
|
543
|
+
});
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Features not syncing
|
|
547
|
+
|
|
548
|
+
If features declared in YAML aren't appearing on the server:
|
|
549
|
+
|
|
550
|
+
1. Check that you provided `privateKey` (read-only mode can't create features)
|
|
551
|
+
2. Check network connectivity to FlipFlag API
|
|
552
|
+
3. Verify your API keys are correct
|
|
553
|
+
4. Check browser/server console for errors
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## 🌐 Platform Compatibility
|
|
558
|
+
|
|
559
|
+
- **Node.js**: v16+ (ESM and CommonJS)
|
|
560
|
+
- **Browsers**: Modern browsers with `fetch` API support
|
|
561
|
+
- **TypeScript**: 4.5+
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## 📦 Package Exports
|
|
566
|
+
|
|
567
|
+
The package provides optimized builds for different environments:
|
|
568
|
+
|
|
569
|
+
```json
|
|
570
|
+
{
|
|
571
|
+
"exports": {
|
|
572
|
+
".": {
|
|
573
|
+
"types": "./dist/browser.d.ts",
|
|
574
|
+
"browser": "./dist/browser.js",
|
|
575
|
+
"node": "./dist/node.js",
|
|
576
|
+
"default": "./dist/browser.js"
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
Node.js automatically uses the Node-specific build with filesystem support, while browsers get the lightweight browser build.
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
## 🔗 Links
|
|
587
|
+
|
|
588
|
+
- **Website**: https://flipflag.dev
|
|
589
|
+
- **Documentation**: https://docs.flipflag.dev
|
|
590
|
+
- **GitHub**: https://github.com/flipflag-dev/sdk
|
|
591
|
+
- **NPM**: https://www.npmjs.com/package/@flipflag/sdk
|
|
592
|
+
- **Issues**: https://github.com/flipflag-dev/sdk/issues
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
## 📄 License
|
|
597
|
+
|
|
598
|
+
MIT License - see [LICENSE](./LICENSE) file for details.
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## 🤝 Contributing
|
|
603
|
+
|
|
604
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
605
|
+
|
|
606
|
+
---
|
|
110
607
|
|
|
111
|
-
|
|
608
|
+
**Made with ❤️ by the FlipFlag team**
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as FlipFlagCore, I as IManagerOptions, a as FlipFlagYaml } from './flipflag-core-
|
|
1
|
+
import { F as FlipFlagCore, I as IManagerOptions, a as FlipFlagYaml } from './flipflag-core-3rbGd7Gt.js';
|
|
2
2
|
|
|
3
3
|
declare class FlipFlag extends FlipFlagCore {
|
|
4
4
|
constructor(opts: IManagerOptions, initialConfig?: FlipFlagYaml);
|
package/dist/browser.js
CHANGED
package/dist/browser.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{F as s}from"./flipflag-core-
|
|
1
|
+
import{F as s}from"./flipflag-core-D9bYmvZd.js";class o extends s{constructor(e,r){super(e),r&&this.declareFromObject(r)}}export{o as FlipFlag};
|
|
2
2
|
//# sourceMappingURL=browser.min.js.map
|
|
@@ -17,6 +17,11 @@ interface IManagerOptions {
|
|
|
17
17
|
* Default: 30000 (30 seconds)
|
|
18
18
|
*/
|
|
19
19
|
pollingInterval?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Sync interval in milliseconds (for syncing times and usage).
|
|
22
|
+
* Default: 90000 (90 seconds)
|
|
23
|
+
*/
|
|
24
|
+
syncInterval?: number;
|
|
20
25
|
}
|
|
21
26
|
type YamlTime = {
|
|
22
27
|
started: string;
|
|
@@ -38,7 +43,8 @@ declare class FlipFlagCore {
|
|
|
38
43
|
protected readonly opts: IManagerOptions;
|
|
39
44
|
private readonly loader?;
|
|
40
45
|
private inited;
|
|
41
|
-
private
|
|
46
|
+
private pollingIntervalTimer;
|
|
47
|
+
private syncIntervalTimer;
|
|
42
48
|
protected options: Partial<IManagerOptions>;
|
|
43
49
|
private featuresTimes;
|
|
44
50
|
private featuresFlags;
|
|
@@ -48,6 +54,7 @@ declare class FlipFlagCore {
|
|
|
48
54
|
destroy(): void;
|
|
49
55
|
isEnabled(featureName: string): boolean;
|
|
50
56
|
declareFromObject(doc: FlipFlagYaml): void;
|
|
57
|
+
sync(): Promise<void>;
|
|
51
58
|
private applyYamlConfig;
|
|
52
59
|
private upsertFeaturesUsage;
|
|
53
60
|
private getBaseUrl;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
class l{constructor(e,t){this.opts=e,this.loader=t,this.inited=!1,this.pollingIntervalTimer=null,this.syncIntervalTimer=null,this.featuresTimes={},this.featuresFlags={},this.featuresUsage=[],this.options={apiUrl:"https://api.flipflag.dev",pollingInterval:3e4,syncInterval:9e4,...e}}async init(){if(this.loader){const e=await this.loader.load();e&&this.applyYamlConfig(e)}await this.getFeaturesFlags(),await this.syncFeaturesTimes(),this.pollingIntervalTimer=setInterval(()=>{this.getFeaturesFlags()},this.options.pollingInterval),this.syncIntervalTimer=setInterval(()=>{this.syncFeaturesTimes(),this.syncFeaturesUsage()},this.options.syncInterval),this.inited=!0}destroy(){this.inited=!1,this.pollingIntervalTimer&&clearInterval(this.pollingIntervalTimer),this.syncIntervalTimer&&clearInterval(this.syncIntervalTimer),this.featuresTimes={},this.featuresFlags={},this.featuresUsage=[]}isEnabled(e){const t=this.featuresFlags[e];return t?(this.upsertFeaturesUsage(e),t.enabled):(this.createFeature(e,{times:[]}),!1)}declareFromObject(e){this.applyYamlConfig(e)}async sync(){await this.syncFeaturesTimes(),await this.syncFeaturesUsage()}applyYamlConfig(e){var t;for(const[s,i]of Object.entries(e)){const r=((t=i?.times)!=null?t:[]).map(a=>{var n;return{email:e[s].contributor,start:a.started,end:(n=a.finished)!=null?n:null}});for(const a of r){if(Number.isNaN(Date.parse(a.start)))throw new Error(`FlipFlag: invalid "started" date in ${s}: ${a.start}`);if(a.end!==null&&Number.isNaN(Date.parse(String(a.end))))throw new Error(`FlipFlag: invalid "finished" date in ${s}: ${a.end}`)}this.featuresTimes[s]={times:r,type:i?.type,description:i?.description}}}upsertFeaturesUsage(e){const t=this.featuresUsage.find(s=>s.featureName===e);if(t){t.usedAt=new Date;return}this.featuresUsage.push({featureName:e,usedAt:new Date})}getBaseUrl(){if(this.options.apiUrl)return this.options.apiUrl.replace(/\/+$/,"");throw new Error("Base API URL is not configured. Please provide apiUrl in the SDK options.")}async createFeature(e,t){if(!this.options.privateKey)return null;const s=new URL("/v1/sdk/feature",this.getBaseUrl());fetch(s.toString(),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({featureName:e,privateKey:this.options.privateKey,...t})}).catch(i=>console.error("Create Feature:",i))}async getFeaturesFlags(){if(!this.options.publicKey)throw new Error("Public key is missing. Please provide a valid publicKey in the SDK configuration.");try{const e=new URL("/v1/sdk/feature/flags",this.getBaseUrl());e.searchParams.append("publicKey",this.options.publicKey);const t=await fetch(e.toString(),{method:"GET",headers:{"Content-Type":"application/json"}});if(!t.ok&&!this.inited){const s=await t.text();throw new Error(`Failed to get features: ${t.status} - ${s}`)}this.featuresFlags=await t.json()}catch(e){console.error("Get list features flag:",e)}}async syncFeaturesTimes(){if(!this.options.privateKey)return null;for(const[e,t]of Object.entries(this.featuresTimes))this.createFeature(e,t)}async syncFeaturesUsage(){if(!this.options.publicKey)throw new Error("Public key is missing. Please provide a valid publicKey in the SDK configuration.");const e=new URL("/v1/sdk/feature/usages",this.getBaseUrl());fetch(e.toString(),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({publicKey:this.options.publicKey,privateKey:this.options.privateKey,usages:this.featuresUsage})}).catch(t=>console.error("Feature Usage Sync:",t))}}export{l as F};
|
|
2
|
+
//# sourceMappingURL=flipflag-core-D9bYmvZd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flipflag-core-D9bYmvZd.js","sources":["../src/core/flipflag-core.ts"],"sourcesContent":["import {\n FlipFlagYaml,\n IDeclareFeatureOptions,\n IDeclareFeatureTime,\n IFeatureFlag,\n IFeatureFlagUsage,\n IManagerOptions,\n} from \"../types/provider\";\nimport { ConfigLoader } from \"../platform/config-loader\";\n\nexport class FlipFlagCore {\n private inited = false;\n private pollingIntervalTimer: any = null;\n private syncIntervalTimer: any = null;\n\n protected options: Partial<IManagerOptions>;\n private featuresTimes: Record<string, IDeclareFeatureOptions> = {};\n private featuresFlags: Record<string, IFeatureFlag> = {};\n private featuresUsage: IFeatureFlagUsage[] = [];\n\n constructor(\n protected readonly opts: IManagerOptions,\n private readonly loader?: ConfigLoader,\n ) {\n this.options = {\n apiUrl: \"https://api.flipflag.dev\",\n pollingInterval: 30_000,\n syncInterval: 90_000,\n ...opts,\n };\n }\n\n public async init() {\n if (this.loader) {\n const yamlDoc = await this.loader.load();\n if (yamlDoc) this.applyYamlConfig(yamlDoc);\n }\n\n await this.getFeaturesFlags();\n await this.syncFeaturesTimes();\n\n this.pollingIntervalTimer = setInterval(() => {\n this.getFeaturesFlags();\n }, this.options.pollingInterval);\n\n this.syncIntervalTimer = setInterval(() => {\n this.syncFeaturesTimes();\n this.syncFeaturesUsage();\n }, this.options.syncInterval);\n\n this.inited = true;\n }\n\n public destroy() {\n this.inited = false;\n if (this.pollingIntervalTimer) clearInterval(this.pollingIntervalTimer);\n if (this.syncIntervalTimer) clearInterval(this.syncIntervalTimer);\n this.featuresTimes = {};\n this.featuresFlags = {};\n this.featuresUsage = [];\n }\n\n public isEnabled(featureName: string) {\n const feature = this.featuresFlags[featureName];\n if (!feature) {\n this.createFeature(featureName, { times: [] });\n return false;\n }\n this.upsertFeaturesUsage(featureName);\n return feature.enabled;\n }\n\n public declareFromObject(doc: FlipFlagYaml) {\n this.applyYamlConfig(doc);\n }\n\n public async sync() {\n await this.syncFeaturesTimes();\n await this.syncFeaturesUsage();\n }\n\n private applyYamlConfig(doc: FlipFlagYaml) {\n for (const [featureName, cfg] of Object.entries(doc)) {\n const times = (cfg?.times ?? []).map((t) => ({\n email: doc[featureName].contributor,\n start: t.started,\n end: t.finished ?? null,\n })) as IDeclareFeatureTime[];\n\n for (const t of times) {\n if (Number.isNaN(Date.parse(t.start))) {\n throw new Error(\n `FlipFlag: invalid \"started\" date in ${featureName}: ${t.start}`,\n );\n }\n if (t.end !== null && Number.isNaN(Date.parse(String(t.end)))) {\n throw new Error(\n `FlipFlag: invalid \"finished\" date in ${featureName}: ${t.end}`,\n );\n }\n }\n\n this.featuresTimes[featureName] = {\n times,\n type: cfg?.type,\n description: cfg?.description,\n };\n }\n }\n\n private upsertFeaturesUsage(featureName: string) {\n const existing = this.featuresUsage.find(\n (u) => u.featureName === featureName,\n );\n if (existing) {\n existing.usedAt = new Date();\n return;\n }\n this.featuresUsage.push({ featureName, usedAt: new Date() });\n }\n\n private getBaseUrl() {\n if (this.options.apiUrl) return this.options.apiUrl.replace(/\\/+$/, \"\");\n throw new Error(\n \"Base API URL is not configured. Please provide apiUrl in the SDK options.\",\n );\n }\n\n private async createFeature(\n featureName: string,\n options: IDeclareFeatureOptions,\n ) {\n if (!this.options.privateKey) return null;\n\n const url = new URL(\"/v1/sdk/feature\", this.getBaseUrl());\n fetch(url.toString(), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n featureName,\n privateKey: this.options.privateKey,\n ...options,\n }),\n }).catch((e) => console.error(\"Create Feature:\", e));\n }\n\n private async getFeaturesFlags() {\n if (!this.options.publicKey) {\n throw new Error(\n \"Public key is missing. Please provide a valid publicKey in the SDK configuration.\",\n );\n }\n\n try {\n const url = new URL(\"/v1/sdk/feature/flags\", this.getBaseUrl());\n url.searchParams.append(\"publicKey\", this.options.publicKey);\n\n const res = await fetch(url.toString(), {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!res.ok && !this.inited) {\n const errorText = await res.text();\n throw new Error(`Failed to get features: ${res.status} - ${errorText}`);\n }\n\n this.featuresFlags = await res.json();\n } catch (e) {\n console.error(\"Get list features flag:\", e);\n }\n }\n\n private async syncFeaturesTimes() {\n if (!this.options.privateKey) return null;\n for (const [featureName, options] of Object.entries(this.featuresTimes)) {\n this.createFeature(featureName, options);\n }\n }\n\n private async syncFeaturesUsage() {\n if (!this.options.publicKey) {\n throw new Error(\n \"Public key is missing. Please provide a valid publicKey in the SDK configuration.\",\n );\n }\n const url = new URL(\"/v1/sdk/feature/usages\", this.getBaseUrl());\n\n fetch(url.toString(), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n publicKey: this.options.publicKey,\n privateKey: this.options.privateKey,\n usages: this.featuresUsage,\n }),\n }).catch((e) => console.error(\"Feature Usage Sync:\", e));\n }\n}\n"],"names":["FlipFlagCore","opts","loader","yamlDoc","featureName","feature","doc","_a","cfg","times","t","existing","u","options","url","e","res","errorText"],"mappings":"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"}
|
|
@@ -3,13 +3,15 @@ class FlipFlagCore {
|
|
|
3
3
|
this.opts = opts;
|
|
4
4
|
this.loader = loader;
|
|
5
5
|
this.inited = false;
|
|
6
|
-
this.
|
|
6
|
+
this.pollingIntervalTimer = null;
|
|
7
|
+
this.syncIntervalTimer = null;
|
|
7
8
|
this.featuresTimes = {};
|
|
8
9
|
this.featuresFlags = {};
|
|
9
10
|
this.featuresUsage = [];
|
|
10
11
|
this.options = {
|
|
11
12
|
apiUrl: "https://api.flipflag.dev",
|
|
12
13
|
pollingInterval: 3e4,
|
|
14
|
+
syncInterval: 9e4,
|
|
13
15
|
...opts
|
|
14
16
|
};
|
|
15
17
|
}
|
|
@@ -20,16 +22,19 @@ class FlipFlagCore {
|
|
|
20
22
|
}
|
|
21
23
|
await this.getFeaturesFlags();
|
|
22
24
|
await this.syncFeaturesTimes();
|
|
23
|
-
this.
|
|
25
|
+
this.pollingIntervalTimer = setInterval(() => {
|
|
24
26
|
this.getFeaturesFlags();
|
|
27
|
+
}, this.options.pollingInterval);
|
|
28
|
+
this.syncIntervalTimer = setInterval(() => {
|
|
25
29
|
this.syncFeaturesTimes();
|
|
26
30
|
this.syncFeaturesUsage();
|
|
27
|
-
}, this.options.
|
|
31
|
+
}, this.options.syncInterval);
|
|
28
32
|
this.inited = true;
|
|
29
33
|
}
|
|
30
34
|
destroy() {
|
|
31
35
|
this.inited = false;
|
|
32
|
-
if (this.
|
|
36
|
+
if (this.pollingIntervalTimer) clearInterval(this.pollingIntervalTimer);
|
|
37
|
+
if (this.syncIntervalTimer) clearInterval(this.syncIntervalTimer);
|
|
33
38
|
this.featuresTimes = {};
|
|
34
39
|
this.featuresFlags = {};
|
|
35
40
|
this.featuresUsage = [];
|
|
@@ -46,6 +51,10 @@ class FlipFlagCore {
|
|
|
46
51
|
declareFromObject(doc) {
|
|
47
52
|
this.applyYamlConfig(doc);
|
|
48
53
|
}
|
|
54
|
+
async sync() {
|
|
55
|
+
await this.syncFeaturesTimes();
|
|
56
|
+
await this.syncFeaturesUsage();
|
|
57
|
+
}
|
|
49
58
|
applyYamlConfig(doc) {
|
|
50
59
|
var _a;
|
|
51
60
|
for (const [featureName, cfg] of Object.entries(doc)) {
|
|
@@ -69,7 +78,11 @@ class FlipFlagCore {
|
|
|
69
78
|
);
|
|
70
79
|
}
|
|
71
80
|
}
|
|
72
|
-
this.featuresTimes[featureName] = {
|
|
81
|
+
this.featuresTimes[featureName] = {
|
|
82
|
+
times,
|
|
83
|
+
type: cfg == null ? void 0 : cfg.type,
|
|
84
|
+
description: cfg == null ? void 0 : cfg.description
|
|
85
|
+
};
|
|
73
86
|
}
|
|
74
87
|
}
|
|
75
88
|
upsertFeaturesUsage(featureName) {
|
|
@@ -149,4 +162,4 @@ class FlipFlagCore {
|
|
|
149
162
|
}
|
|
150
163
|
|
|
151
164
|
export { FlipFlagCore as F };
|
|
152
|
-
//# sourceMappingURL=flipflag-core-
|
|
165
|
+
//# sourceMappingURL=flipflag-core-DzEU3Qfu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flipflag-core-DzEU3Qfu.js","sources":["../src/core/flipflag-core.ts"],"sourcesContent":["import {\n FlipFlagYaml,\n IDeclareFeatureOptions,\n IDeclareFeatureTime,\n IFeatureFlag,\n IFeatureFlagUsage,\n IManagerOptions,\n} from \"../types/provider\";\nimport { ConfigLoader } from \"../platform/config-loader\";\n\nexport class FlipFlagCore {\n private inited = false;\n private pollingIntervalTimer: any = null;\n private syncIntervalTimer: any = null;\n\n protected options: Partial<IManagerOptions>;\n private featuresTimes: Record<string, IDeclareFeatureOptions> = {};\n private featuresFlags: Record<string, IFeatureFlag> = {};\n private featuresUsage: IFeatureFlagUsage[] = [];\n\n constructor(\n protected readonly opts: IManagerOptions,\n private readonly loader?: ConfigLoader,\n ) {\n this.options = {\n apiUrl: \"https://api.flipflag.dev\",\n pollingInterval: 30_000,\n syncInterval: 90_000,\n ...opts,\n };\n }\n\n public async init() {\n if (this.loader) {\n const yamlDoc = await this.loader.load();\n if (yamlDoc) this.applyYamlConfig(yamlDoc);\n }\n\n await this.getFeaturesFlags();\n await this.syncFeaturesTimes();\n\n this.pollingIntervalTimer = setInterval(() => {\n this.getFeaturesFlags();\n }, this.options.pollingInterval);\n\n this.syncIntervalTimer = setInterval(() => {\n this.syncFeaturesTimes();\n this.syncFeaturesUsage();\n }, this.options.syncInterval);\n\n this.inited = true;\n }\n\n public destroy() {\n this.inited = false;\n if (this.pollingIntervalTimer) clearInterval(this.pollingIntervalTimer);\n if (this.syncIntervalTimer) clearInterval(this.syncIntervalTimer);\n this.featuresTimes = {};\n this.featuresFlags = {};\n this.featuresUsage = [];\n }\n\n public isEnabled(featureName: string) {\n const feature = this.featuresFlags[featureName];\n if (!feature) {\n this.createFeature(featureName, { times: [] });\n return false;\n }\n this.upsertFeaturesUsage(featureName);\n return feature.enabled;\n }\n\n public declareFromObject(doc: FlipFlagYaml) {\n this.applyYamlConfig(doc);\n }\n\n public async sync() {\n await this.syncFeaturesTimes();\n await this.syncFeaturesUsage();\n }\n\n private applyYamlConfig(doc: FlipFlagYaml) {\n for (const [featureName, cfg] of Object.entries(doc)) {\n const times = (cfg?.times ?? []).map((t) => ({\n email: doc[featureName].contributor,\n start: t.started,\n end: t.finished ?? null,\n })) as IDeclareFeatureTime[];\n\n for (const t of times) {\n if (Number.isNaN(Date.parse(t.start))) {\n throw new Error(\n `FlipFlag: invalid \"started\" date in ${featureName}: ${t.start}`,\n );\n }\n if (t.end !== null && Number.isNaN(Date.parse(String(t.end)))) {\n throw new Error(\n `FlipFlag: invalid \"finished\" date in ${featureName}: ${t.end}`,\n );\n }\n }\n\n this.featuresTimes[featureName] = {\n times,\n type: cfg?.type,\n description: cfg?.description,\n };\n }\n }\n\n private upsertFeaturesUsage(featureName: string) {\n const existing = this.featuresUsage.find(\n (u) => u.featureName === featureName,\n );\n if (existing) {\n existing.usedAt = new Date();\n return;\n }\n this.featuresUsage.push({ featureName, usedAt: new Date() });\n }\n\n private getBaseUrl() {\n if (this.options.apiUrl) return this.options.apiUrl.replace(/\\/+$/, \"\");\n throw new Error(\n \"Base API URL is not configured. Please provide apiUrl in the SDK options.\",\n );\n }\n\n private async createFeature(\n featureName: string,\n options: IDeclareFeatureOptions,\n ) {\n if (!this.options.privateKey) return null;\n\n const url = new URL(\"/v1/sdk/feature\", this.getBaseUrl());\n fetch(url.toString(), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n featureName,\n privateKey: this.options.privateKey,\n ...options,\n }),\n }).catch((e) => console.error(\"Create Feature:\", e));\n }\n\n private async getFeaturesFlags() {\n if (!this.options.publicKey) {\n throw new Error(\n \"Public key is missing. Please provide a valid publicKey in the SDK configuration.\",\n );\n }\n\n try {\n const url = new URL(\"/v1/sdk/feature/flags\", this.getBaseUrl());\n url.searchParams.append(\"publicKey\", this.options.publicKey);\n\n const res = await fetch(url.toString(), {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!res.ok && !this.inited) {\n const errorText = await res.text();\n throw new Error(`Failed to get features: ${res.status} - ${errorText}`);\n }\n\n this.featuresFlags = await res.json();\n } catch (e) {\n console.error(\"Get list features flag:\", e);\n }\n }\n\n private async syncFeaturesTimes() {\n if (!this.options.privateKey) return null;\n for (const [featureName, options] of Object.entries(this.featuresTimes)) {\n this.createFeature(featureName, options);\n }\n }\n\n private async syncFeaturesUsage() {\n if (!this.options.publicKey) {\n throw new Error(\n \"Public key is missing. Please provide a valid publicKey in the SDK configuration.\",\n );\n }\n const url = new URL(\"/v1/sdk/feature/usages\", this.getBaseUrl());\n\n fetch(url.toString(), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n publicKey: this.options.publicKey,\n privateKey: this.options.privateKey,\n usages: this.featuresUsage,\n }),\n }).catch((e) => console.error(\"Feature Usage Sync:\", e));\n }\n}\n"],"names":["_a"],"mappings":"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"}
|
package/dist/node.d.ts
CHANGED
package/dist/node.js
CHANGED
package/dist/node.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{F as s}from"./flipflag-core-
|
|
1
|
+
import{F as s}from"./flipflag-core-D9bYmvZd.js";import*as c from"node:fs/promises";import*as p from"node:path";import*as f from"js-yaml";class g{constructor(r){this.options=r}async load(){var r,t,e;const l=(r=this.options.configPath)!=null?r:p.resolve(process.cwd(),".flipflag.yml");let n;try{n=await c.readFile(l,"utf8")}catch(o){if(o?.code==="ENOENT"&&this.options.ignoreMissingConfig)return null;throw new Error(`FlipFlag: cannot read config at ${l}: ${(t=o?.message)!=null?t:o}`)}let a;try{a=f.load(n)}catch(o){throw new Error(`FlipFlag: invalid YAML in ${l}: ${(e=o?.message)!=null?e:o}`)}return!a||typeof a!="object"||Array.isArray(a)?(console.warn("FlipFlag: YAML root must be an object (mapping featureName -> config)"),null):a}}class u extends s{constructor(r){super(r,new g(r))}}export{u as FlipFlag};
|
|
2
2
|
//# sourceMappingURL=node.min.js.map
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"flipflag-core-BNqlFhCW.js","sources":["../src/core/flipflag-core.ts"],"sourcesContent":["import {\n FlipFlagYaml,\n IDeclareFeatureOptions,\n IDeclareFeatureTime,\n IFeatureFlag,\n IFeatureFlagUsage,\n IManagerOptions,\n} from \"../types/provider\";\nimport { ConfigLoader } from \"../platform/config-loader\";\n\nexport class FlipFlagCore {\n private inited = false;\n private interval: any = null;\n\n protected options: Partial<IManagerOptions>;\n private featuresTimes: Record<string, IDeclareFeatureOptions> = {};\n private featuresFlags: Record<string, IFeatureFlag> = {};\n private featuresUsage: IFeatureFlagUsage[] = [];\n\n constructor(\n protected readonly opts: IManagerOptions,\n private readonly loader?: ConfigLoader,\n ) {\n this.options = {\n apiUrl: \"https://api.flipflag.dev\",\n pollingInterval: 30_000,\n ...opts,\n };\n }\n\n public async init() {\n if (this.loader) {\n const yamlDoc = await this.loader.load();\n if (yamlDoc) this.applyYamlConfig(yamlDoc);\n }\n\n await this.getFeaturesFlags();\n await this.syncFeaturesTimes();\n\n this.interval = setInterval(() => {\n this.getFeaturesFlags();\n this.syncFeaturesTimes();\n this.syncFeaturesUsage();\n }, this.options.pollingInterval);\n\n this.inited = true;\n }\n\n public destroy() {\n this.inited = false;\n if (this.interval) clearInterval(this.interval);\n this.featuresTimes = {};\n this.featuresFlags = {};\n this.featuresUsage = [];\n }\n\n public isEnabled(featureName: string) {\n const feature = this.featuresFlags[featureName];\n if (!feature) {\n this.createFeature(featureName, { times: [] });\n return false;\n }\n this.upsertFeaturesUsage(featureName);\n return feature.enabled;\n }\n\n public declareFromObject(doc: FlipFlagYaml) {\n this.applyYamlConfig(doc);\n }\n\n private applyYamlConfig(doc: FlipFlagYaml) {\n for (const [featureName, cfg] of Object.entries(doc)) {\n const times = (cfg?.times ?? []).map((t) => ({\n email: doc[featureName].contributor,\n start: t.started,\n end: t.finished ?? null,\n })) as IDeclareFeatureTime[];\n\n for (const t of times) {\n if (Number.isNaN(Date.parse(t.start))) {\n throw new Error(\n `FlipFlag: invalid \"started\" date in ${featureName}: ${t.start}`,\n );\n }\n if (t.end !== null && Number.isNaN(Date.parse(String(t.end)))) {\n throw new Error(\n `FlipFlag: invalid \"finished\" date in ${featureName}: ${t.end}`,\n );\n }\n }\n\n this.featuresTimes[featureName] = { times };\n }\n }\n\n private upsertFeaturesUsage(featureName: string) {\n const existing = this.featuresUsage.find(\n (u) => u.featureName === featureName,\n );\n if (existing) {\n existing.usedAt = new Date();\n return;\n }\n this.featuresUsage.push({ featureName, usedAt: new Date() });\n }\n\n private getBaseUrl() {\n if (this.options.apiUrl) return this.options.apiUrl.replace(/\\/+$/, \"\");\n throw new Error(\n \"Base API URL is not configured. Please provide apiUrl in the SDK options.\",\n );\n }\n\n private async createFeature(\n featureName: string,\n options: IDeclareFeatureOptions,\n ) {\n if (!this.options.privateKey) return null;\n\n const url = new URL(\"/v1/sdk/feature\", this.getBaseUrl());\n fetch(url.toString(), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n featureName,\n privateKey: this.options.privateKey,\n ...options,\n }),\n }).catch((e) => console.error(\"Create Feature:\", e));\n }\n\n private async getFeaturesFlags() {\n if (!this.options.publicKey) {\n throw new Error(\n \"Public key is missing. Please provide a valid publicKey in the SDK configuration.\",\n );\n }\n\n try {\n const url = new URL(\"/v1/sdk/feature/flags\", this.getBaseUrl());\n url.searchParams.append(\"publicKey\", this.options.publicKey);\n\n const res = await fetch(url.toString(), {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!res.ok && !this.inited) {\n const errorText = await res.text();\n throw new Error(`Failed to get features: ${res.status} - ${errorText}`);\n }\n\n this.featuresFlags = await res.json();\n } catch (e) {\n console.error(\"Get list features flag:\", e);\n }\n }\n\n private async syncFeaturesTimes() {\n if (!this.options.privateKey) return null;\n for (const [featureName, options] of Object.entries(this.featuresTimes)) {\n this.createFeature(featureName, options);\n }\n }\n\n private async syncFeaturesUsage() {\n if (!this.options.publicKey) {\n throw new Error(\n \"Public key is missing. Please provide a valid publicKey in the SDK configuration.\",\n );\n }\n const url = new URL(\"/v1/sdk/feature/usages\", this.getBaseUrl());\n\n fetch(url.toString(), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n publicKey: this.options.publicKey,\n privateKey: this.options.privateKey,\n usages: this.featuresUsage,\n }),\n }).catch((e) => console.error(\"Feature Usage Sync:\", e));\n }\n}\n"],"names":["_a"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
class o{constructor(e,t){this.opts=e,this.loader=t,this.inited=!1,this.interval=null,this.featuresTimes={},this.featuresFlags={},this.featuresUsage=[],this.options={apiUrl:"https://api.flipflag.dev",pollingInterval:3e4,...e}}async init(){if(this.loader){const e=await this.loader.load();e&&this.applyYamlConfig(e)}await this.getFeaturesFlags(),await this.syncFeaturesTimes(),this.interval=setInterval(()=>{this.getFeaturesFlags(),this.syncFeaturesTimes(),this.syncFeaturesUsage()},this.options.pollingInterval),this.inited=!0}destroy(){this.inited=!1,this.interval&&clearInterval(this.interval),this.featuresTimes={},this.featuresFlags={},this.featuresUsage=[]}isEnabled(e){const t=this.featuresFlags[e];return t?(this.upsertFeaturesUsage(e),t.enabled):(this.createFeature(e,{times:[]}),!1)}declareFromObject(e){this.applyYamlConfig(e)}applyYamlConfig(e){var t;for(const[s,a]of Object.entries(e)){const r=((t=a?.times)!=null?t:[]).map(i=>{var n;return{email:e[s].contributor,start:i.started,end:(n=i.finished)!=null?n:null}});for(const i of r){if(Number.isNaN(Date.parse(i.start)))throw new Error(`FlipFlag: invalid "started" date in ${s}: ${i.start}`);if(i.end!==null&&Number.isNaN(Date.parse(String(i.end))))throw new Error(`FlipFlag: invalid "finished" date in ${s}: ${i.end}`)}this.featuresTimes[s]={times:r}}}upsertFeaturesUsage(e){const t=this.featuresUsage.find(s=>s.featureName===e);if(t){t.usedAt=new Date;return}this.featuresUsage.push({featureName:e,usedAt:new Date})}getBaseUrl(){if(this.options.apiUrl)return this.options.apiUrl.replace(/\/+$/,"");throw new Error("Base API URL is not configured. Please provide apiUrl in the SDK options.")}async createFeature(e,t){if(!this.options.privateKey)return null;const s=new URL("/v1/sdk/feature",this.getBaseUrl());fetch(s.toString(),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({featureName:e,privateKey:this.options.privateKey,...t})}).catch(a=>console.error("Create Feature:",a))}async getFeaturesFlags(){if(!this.options.publicKey)throw new Error("Public key is missing. Please provide a valid publicKey in the SDK configuration.");try{const e=new URL("/v1/sdk/feature/flags",this.getBaseUrl());e.searchParams.append("publicKey",this.options.publicKey);const t=await fetch(e.toString(),{method:"GET",headers:{"Content-Type":"application/json"}});if(!t.ok&&!this.inited){const s=await t.text();throw new Error(`Failed to get features: ${t.status} - ${s}`)}this.featuresFlags=await t.json()}catch(e){console.error("Get list features flag:",e)}}async syncFeaturesTimes(){if(!this.options.privateKey)return null;for(const[e,t]of Object.entries(this.featuresTimes))this.createFeature(e,t)}async syncFeaturesUsage(){if(!this.options.publicKey)throw new Error("Public key is missing. Please provide a valid publicKey in the SDK configuration.");const e=new URL("/v1/sdk/feature/usages",this.getBaseUrl());fetch(e.toString(),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({publicKey:this.options.publicKey,privateKey:this.options.privateKey,usages:this.featuresUsage})}).catch(t=>console.error("Feature Usage Sync:",t))}}export{o as F};
|
|
2
|
-
//# sourceMappingURL=flipflag-core-DHCsP2TB.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"flipflag-core-DHCsP2TB.js","sources":["../src/core/flipflag-core.ts"],"sourcesContent":["import {\n FlipFlagYaml,\n IDeclareFeatureOptions,\n IDeclareFeatureTime,\n IFeatureFlag,\n IFeatureFlagUsage,\n IManagerOptions,\n} from \"../types/provider\";\nimport { ConfigLoader } from \"../platform/config-loader\";\n\nexport class FlipFlagCore {\n private inited = false;\n private interval: any = null;\n\n protected options: Partial<IManagerOptions>;\n private featuresTimes: Record<string, IDeclareFeatureOptions> = {};\n private featuresFlags: Record<string, IFeatureFlag> = {};\n private featuresUsage: IFeatureFlagUsage[] = [];\n\n constructor(\n protected readonly opts: IManagerOptions,\n private readonly loader?: ConfigLoader,\n ) {\n this.options = {\n apiUrl: \"https://api.flipflag.dev\",\n pollingInterval: 30_000,\n ...opts,\n };\n }\n\n public async init() {\n if (this.loader) {\n const yamlDoc = await this.loader.load();\n if (yamlDoc) this.applyYamlConfig(yamlDoc);\n }\n\n await this.getFeaturesFlags();\n await this.syncFeaturesTimes();\n\n this.interval = setInterval(() => {\n this.getFeaturesFlags();\n this.syncFeaturesTimes();\n this.syncFeaturesUsage();\n }, this.options.pollingInterval);\n\n this.inited = true;\n }\n\n public destroy() {\n this.inited = false;\n if (this.interval) clearInterval(this.interval);\n this.featuresTimes = {};\n this.featuresFlags = {};\n this.featuresUsage = [];\n }\n\n public isEnabled(featureName: string) {\n const feature = this.featuresFlags[featureName];\n if (!feature) {\n this.createFeature(featureName, { times: [] });\n return false;\n }\n this.upsertFeaturesUsage(featureName);\n return feature.enabled;\n }\n\n public declareFromObject(doc: FlipFlagYaml) {\n this.applyYamlConfig(doc);\n }\n\n private applyYamlConfig(doc: FlipFlagYaml) {\n for (const [featureName, cfg] of Object.entries(doc)) {\n const times = (cfg?.times ?? []).map((t) => ({\n email: doc[featureName].contributor,\n start: t.started,\n end: t.finished ?? null,\n })) as IDeclareFeatureTime[];\n\n for (const t of times) {\n if (Number.isNaN(Date.parse(t.start))) {\n throw new Error(\n `FlipFlag: invalid \"started\" date in ${featureName}: ${t.start}`,\n );\n }\n if (t.end !== null && Number.isNaN(Date.parse(String(t.end)))) {\n throw new Error(\n `FlipFlag: invalid \"finished\" date in ${featureName}: ${t.end}`,\n );\n }\n }\n\n this.featuresTimes[featureName] = { times };\n }\n }\n\n private upsertFeaturesUsage(featureName: string) {\n const existing = this.featuresUsage.find(\n (u) => u.featureName === featureName,\n );\n if (existing) {\n existing.usedAt = new Date();\n return;\n }\n this.featuresUsage.push({ featureName, usedAt: new Date() });\n }\n\n private getBaseUrl() {\n if (this.options.apiUrl) return this.options.apiUrl.replace(/\\/+$/, \"\");\n throw new Error(\n \"Base API URL is not configured. Please provide apiUrl in the SDK options.\",\n );\n }\n\n private async createFeature(\n featureName: string,\n options: IDeclareFeatureOptions,\n ) {\n if (!this.options.privateKey) return null;\n\n const url = new URL(\"/v1/sdk/feature\", this.getBaseUrl());\n fetch(url.toString(), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n featureName,\n privateKey: this.options.privateKey,\n ...options,\n }),\n }).catch((e) => console.error(\"Create Feature:\", e));\n }\n\n private async getFeaturesFlags() {\n if (!this.options.publicKey) {\n throw new Error(\n \"Public key is missing. Please provide a valid publicKey in the SDK configuration.\",\n );\n }\n\n try {\n const url = new URL(\"/v1/sdk/feature/flags\", this.getBaseUrl());\n url.searchParams.append(\"publicKey\", this.options.publicKey);\n\n const res = await fetch(url.toString(), {\n method: \"GET\",\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!res.ok && !this.inited) {\n const errorText = await res.text();\n throw new Error(`Failed to get features: ${res.status} - ${errorText}`);\n }\n\n this.featuresFlags = await res.json();\n } catch (e) {\n console.error(\"Get list features flag:\", e);\n }\n }\n\n private async syncFeaturesTimes() {\n if (!this.options.privateKey) return null;\n for (const [featureName, options] of Object.entries(this.featuresTimes)) {\n this.createFeature(featureName, options);\n }\n }\n\n private async syncFeaturesUsage() {\n if (!this.options.publicKey) {\n throw new Error(\n \"Public key is missing. Please provide a valid publicKey in the SDK configuration.\",\n );\n }\n const url = new URL(\"/v1/sdk/feature/usages\", this.getBaseUrl());\n\n fetch(url.toString(), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n publicKey: this.options.publicKey,\n privateKey: this.options.privateKey,\n usages: this.featuresUsage,\n }),\n }).catch((e) => console.error(\"Feature Usage Sync:\", e));\n }\n}\n"],"names":["FlipFlagCore","opts","loader","yamlDoc","featureName","feature","doc","_a","cfg","times","t","existing","u","options","url","e","res","errorText"],"mappings":"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"}
|