@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 CHANGED
@@ -1,37 +1,26 @@
1
1
  # FlipFlag SDK
2
2
 
3
- A lightweight client-side SDK for working with **FlipFlag** (https://flipflag.dev).
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
- ## Features
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
- ## Configuration via `.flipflag.yml`
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
- By default, the SDK looks for a `.flipflag.yml` file in the project root
56
- (`process.cwd()`), which is loaded during `init()`.
149
+ // Check feature state
150
+ if (manager.isEnabled("darkMode")) {
151
+ enableDarkMode();
152
+ }
57
153
 
58
- ### Example `.flipflag.yml`
154
+ // Cleanup when done
155
+ manager.destroy();
156
+ ```
59
157
 
60
- ```yml
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
- ## Checking Feature State
305
+ ### Custom Config Path
76
306
 
77
307
  ```ts
78
- manager.isEnabled("newFeature");
308
+ const manager = new FlipFlag({
309
+ publicKey: "pub_xxxxxxxxxxxxx",
310
+ configPath: "./config/features.yml",
311
+ });
79
312
  ```
80
313
 
81
314
  ---
82
315
 
83
- ## Automatic Syncing
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
- After calling `init()`, the SDK starts a **10-second polling loop**.
379
+ ##### `destroy(): void`
86
380
 
87
- To stop syncing and clear all local state:
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
- ## Manager Options
392
+ ## 🎨 TypeScript Types
393
+
394
+ ### `FlipFlagYaml`
96
395
 
97
396
  ```ts
98
- export interface IManagerOptions {
99
- apiUrl?: string;
100
- publicKey: string;
101
- privateKey?: string;
102
- configPath?: string;
103
- ignoreMissingConfig?: boolean;
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
- ## License
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
- MIT License
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-v7AUprKd.js';
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
@@ -1,4 +1,4 @@
1
- import { F as FlipFlagCore } from './flipflag-core-BNqlFhCW.js';
1
+ import { F as FlipFlagCore } from './flipflag-core-DzEU3Qfu.js';
2
2
 
3
3
  class FlipFlag extends FlipFlagCore {
4
4
  constructor(opts, initialConfig) {
@@ -1,2 +1,2 @@
1
- import{F as s}from"./flipflag-core-DHCsP2TB.js";class o extends s{constructor(e,r){super(e),r&&this.declareFromObject(r)}}export{o as FlipFlag};
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 interval;
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.interval = null;
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.interval = setInterval(() => {
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.pollingInterval);
31
+ }, this.options.syncInterval);
28
32
  this.inited = true;
29
33
  }
30
34
  destroy() {
31
35
  this.inited = false;
32
- if (this.interval) clearInterval(this.interval);
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] = { times };
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-BNqlFhCW.js.map
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
@@ -1,4 +1,4 @@
1
- import { F as FlipFlagCore, I as IManagerOptions } from './flipflag-core-v7AUprKd.js';
1
+ import { F as FlipFlagCore, I as IManagerOptions } from './flipflag-core-3rbGd7Gt.js';
2
2
 
3
3
  declare class FlipFlag extends FlipFlagCore {
4
4
  constructor(opts: IManagerOptions);
package/dist/node.js CHANGED
@@ -1,4 +1,4 @@
1
- import { F as FlipFlagCore } from './flipflag-core-BNqlFhCW.js';
1
+ import { F as FlipFlagCore } from './flipflag-core-DzEU3Qfu.js';
2
2
  import * as fs from 'node:fs/promises';
3
3
  import * as path from 'node:path';
4
4
  import * as yaml from 'js-yaml';
package/dist/node.min.js CHANGED
@@ -1,2 +1,2 @@
1
- import{F as s}from"./flipflag-core-DHCsP2TB.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};
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@flipflag/sdk",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "description": "A lightweight client-side SDK for working with FlipFlag",
6
6
  "exports": {
@@ -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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"}