@flipfeatureflag/core 0.1.5 → 0.1.7

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/dist/index.d.mts CHANGED
@@ -102,6 +102,10 @@ declare class FlipFlagClient {
102
102
  private metricsTimer?;
103
103
  private metricsBuffer;
104
104
  private context;
105
+ private started;
106
+ private refreshInFlight?;
107
+ private pendingFlagKeys;
108
+ private pendingFullRefresh;
105
109
  constructor(options: FlipFlagClientOptions);
106
110
  start(): Promise<void>;
107
111
  stop(): void;
package/dist/index.d.ts CHANGED
@@ -102,6 +102,10 @@ declare class FlipFlagClient {
102
102
  private metricsTimer?;
103
103
  private metricsBuffer;
104
104
  private context;
105
+ private started;
106
+ private refreshInFlight?;
107
+ private pendingFlagKeys;
108
+ private pendingFullRefresh;
105
109
  constructor(options: FlipFlagClientOptions);
106
110
  start(): Promise<void>;
107
111
  stop(): void;
package/dist/index.js CHANGED
@@ -139,6 +139,9 @@ var FlipFlagClient = class {
139
139
  this.status = { ready: false };
140
140
  this.evaluations = /* @__PURE__ */ new Map();
141
141
  this.metricsBuffer = new MetricsBuffer();
142
+ this.started = false;
143
+ this.pendingFlagKeys = /* @__PURE__ */ new Set();
144
+ this.pendingFullRefresh = false;
142
145
  if (!options.url) {
143
146
  throw new Error("flipFeatureFlag SDK requires url");
144
147
  }
@@ -159,6 +162,10 @@ var FlipFlagClient = class {
159
162
  });
160
163
  }
161
164
  async start() {
165
+ if (this.started) {
166
+ return;
167
+ }
168
+ this.started = true;
162
169
  await this.refresh();
163
170
  if (!this.options.disableRefresh) {
164
171
  this.refreshTimer = setInterval(() => {
@@ -180,6 +187,7 @@ var FlipFlagClient = class {
180
187
  clearInterval(this.metricsTimer);
181
188
  this.metricsTimer = void 0;
182
189
  }
190
+ this.started = false;
183
191
  }
184
192
  on(event, listener) {
185
193
  this.emitter.on(event, listener);
@@ -223,7 +231,9 @@ var FlipFlagClient = class {
223
231
  if (cached) {
224
232
  return this.recordEvaluation(flagKey, cached);
225
233
  }
226
- void this.refresh([flagKey]);
234
+ if (!this.options.disableRefresh) {
235
+ void this.refresh([flagKey]);
236
+ }
227
237
  return this.recordEvaluation(flagKey, {
228
238
  value: defaultValue,
229
239
  variationKey: "default",
@@ -238,25 +248,46 @@ var FlipFlagClient = class {
238
248
  return evaluation;
239
249
  }
240
250
  async refresh(flagKeys) {
241
- try {
242
- const response = await this.http.evaluate({
243
- env: this.options.env,
244
- context: this.context,
245
- flagKeys
246
- });
247
- this.applyEvaluate(response);
248
- if (!this.status.ready) {
249
- this.status.ready = true;
250
- this.emitter.emit("ready");
251
+ if (flagKeys && flagKeys.length > 0) {
252
+ for (const key of flagKeys) {
253
+ this.pendingFlagKeys.add(key);
251
254
  }
252
- this.emitter.emit("update");
253
- } catch (error) {
254
- this.status.error = error;
255
- this.emitter.emit("error", error);
255
+ } else {
256
+ this.pendingFullRefresh = true;
257
+ }
258
+ if (this.refreshInFlight) {
259
+ return this.refreshInFlight;
256
260
  }
261
+ this.refreshInFlight = (async () => {
262
+ const shouldFull = this.pendingFullRefresh;
263
+ const keys = shouldFull ? void 0 : Array.from(this.pendingFlagKeys);
264
+ this.pendingFlagKeys.clear();
265
+ this.pendingFullRefresh = false;
266
+ try {
267
+ const response = await this.http.evaluate({
268
+ env: this.options.env,
269
+ context: this.context,
270
+ flagKeys: keys
271
+ });
272
+ this.applyEvaluate(response);
273
+ if (!this.status.ready) {
274
+ this.status.ready = true;
275
+ this.emitter.emit("ready");
276
+ }
277
+ this.emitter.emit("update");
278
+ } catch (error) {
279
+ this.status.error = error;
280
+ this.emitter.emit("error", error);
281
+ } finally {
282
+ this.refreshInFlight = void 0;
283
+ }
284
+ if (this.pendingFullRefresh || this.pendingFlagKeys.size > 0) {
285
+ return this.refresh();
286
+ }
287
+ })();
288
+ return this.refreshInFlight;
257
289
  }
258
290
  applyEvaluate(response) {
259
- this.evaluations.clear();
260
291
  for (const [key, evaluation] of Object.entries(response.flags)) {
261
292
  this.evaluations.set(key, evaluation);
262
293
  }
package/dist/index.mjs CHANGED
@@ -113,6 +113,9 @@ var FlipFlagClient = class {
113
113
  this.status = { ready: false };
114
114
  this.evaluations = /* @__PURE__ */ new Map();
115
115
  this.metricsBuffer = new MetricsBuffer();
116
+ this.started = false;
117
+ this.pendingFlagKeys = /* @__PURE__ */ new Set();
118
+ this.pendingFullRefresh = false;
116
119
  if (!options.url) {
117
120
  throw new Error("flipFeatureFlag SDK requires url");
118
121
  }
@@ -133,6 +136,10 @@ var FlipFlagClient = class {
133
136
  });
134
137
  }
135
138
  async start() {
139
+ if (this.started) {
140
+ return;
141
+ }
142
+ this.started = true;
136
143
  await this.refresh();
137
144
  if (!this.options.disableRefresh) {
138
145
  this.refreshTimer = setInterval(() => {
@@ -154,6 +161,7 @@ var FlipFlagClient = class {
154
161
  clearInterval(this.metricsTimer);
155
162
  this.metricsTimer = void 0;
156
163
  }
164
+ this.started = false;
157
165
  }
158
166
  on(event, listener) {
159
167
  this.emitter.on(event, listener);
@@ -197,7 +205,9 @@ var FlipFlagClient = class {
197
205
  if (cached) {
198
206
  return this.recordEvaluation(flagKey, cached);
199
207
  }
200
- void this.refresh([flagKey]);
208
+ if (!this.options.disableRefresh) {
209
+ void this.refresh([flagKey]);
210
+ }
201
211
  return this.recordEvaluation(flagKey, {
202
212
  value: defaultValue,
203
213
  variationKey: "default",
@@ -212,25 +222,46 @@ var FlipFlagClient = class {
212
222
  return evaluation;
213
223
  }
214
224
  async refresh(flagKeys) {
215
- try {
216
- const response = await this.http.evaluate({
217
- env: this.options.env,
218
- context: this.context,
219
- flagKeys
220
- });
221
- this.applyEvaluate(response);
222
- if (!this.status.ready) {
223
- this.status.ready = true;
224
- this.emitter.emit("ready");
225
+ if (flagKeys && flagKeys.length > 0) {
226
+ for (const key of flagKeys) {
227
+ this.pendingFlagKeys.add(key);
225
228
  }
226
- this.emitter.emit("update");
227
- } catch (error) {
228
- this.status.error = error;
229
- this.emitter.emit("error", error);
229
+ } else {
230
+ this.pendingFullRefresh = true;
231
+ }
232
+ if (this.refreshInFlight) {
233
+ return this.refreshInFlight;
230
234
  }
235
+ this.refreshInFlight = (async () => {
236
+ const shouldFull = this.pendingFullRefresh;
237
+ const keys = shouldFull ? void 0 : Array.from(this.pendingFlagKeys);
238
+ this.pendingFlagKeys.clear();
239
+ this.pendingFullRefresh = false;
240
+ try {
241
+ const response = await this.http.evaluate({
242
+ env: this.options.env,
243
+ context: this.context,
244
+ flagKeys: keys
245
+ });
246
+ this.applyEvaluate(response);
247
+ if (!this.status.ready) {
248
+ this.status.ready = true;
249
+ this.emitter.emit("ready");
250
+ }
251
+ this.emitter.emit("update");
252
+ } catch (error) {
253
+ this.status.error = error;
254
+ this.emitter.emit("error", error);
255
+ } finally {
256
+ this.refreshInFlight = void 0;
257
+ }
258
+ if (this.pendingFullRefresh || this.pendingFlagKeys.size > 0) {
259
+ return this.refresh();
260
+ }
261
+ })();
262
+ return this.refreshInFlight;
231
263
  }
232
264
  applyEvaluate(response) {
233
- this.evaluations.clear();
234
265
  for (const [key, evaluation] of Object.entries(response.flags)) {
235
266
  this.evaluations.set(key, evaluation);
236
267
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flipfeatureflag/core",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "flipFeatureFlag core SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
package/src/client.ts CHANGED
@@ -26,6 +26,10 @@ export class FlipFlagClient {
26
26
  private metricsTimer?: ReturnType<typeof setInterval>;
27
27
  private metricsBuffer = new MetricsBuffer();
28
28
  private context: SdkContext;
29
+ private started = false;
30
+ private refreshInFlight?: Promise<void>;
31
+ private pendingFlagKeys = new Set<string>();
32
+ private pendingFullRefresh = false;
29
33
 
30
34
  constructor(options: FlipFlagClientOptions) {
31
35
  if (!options.url) {
@@ -52,6 +56,10 @@ export class FlipFlagClient {
52
56
  }
53
57
 
54
58
  async start(): Promise<void> {
59
+ if (this.started) {
60
+ return;
61
+ }
62
+ this.started = true;
55
63
  await this.refresh();
56
64
 
57
65
  if (!this.options.disableRefresh) {
@@ -76,6 +84,7 @@ export class FlipFlagClient {
76
84
  clearInterval(this.metricsTimer);
77
85
  this.metricsTimer = undefined;
78
86
  }
87
+ this.started = false;
79
88
  }
80
89
 
81
90
  on(event: FlipFlagEvent, listener: FlipFlagListener) {
@@ -129,7 +138,9 @@ export class FlipFlagClient {
129
138
  return this.recordEvaluation(flagKey, cached);
130
139
  }
131
140
 
132
- void this.refresh([flagKey]);
141
+ if (!this.options.disableRefresh) {
142
+ void this.refresh([flagKey]);
143
+ }
133
144
  return this.recordEvaluation(flagKey, {
134
145
  value: defaultValue,
135
146
  variationKey: "default",
@@ -145,29 +156,55 @@ export class FlipFlagClient {
145
156
  return evaluation;
146
157
  }
147
158
 
148
- private async refresh(flagKeys?: string[]) {
149
- try {
150
- const response = await this.http.evaluate({
151
- env: this.options.env,
152
- context: this.context,
153
- flagKeys,
154
- });
155
- this.applyEvaluate(response);
156
-
157
- if (!this.status.ready) {
158
- this.status.ready = true;
159
- this.emitter.emit("ready");
159
+ private async refresh(flagKeys?: string[]): Promise<void> {
160
+ if (flagKeys && flagKeys.length > 0) {
161
+ for (const key of flagKeys) {
162
+ this.pendingFlagKeys.add(key);
160
163
  }
164
+ } else {
165
+ this.pendingFullRefresh = true;
166
+ }
161
167
 
162
- this.emitter.emit("update");
163
- } catch (error) {
164
- this.status.error = error as Error;
165
- this.emitter.emit("error", error);
168
+ if (this.refreshInFlight) {
169
+ return this.refreshInFlight;
166
170
  }
171
+
172
+ this.refreshInFlight = (async () => {
173
+ const shouldFull = this.pendingFullRefresh;
174
+ const keys = shouldFull ? undefined : Array.from(this.pendingFlagKeys);
175
+ this.pendingFlagKeys.clear();
176
+ this.pendingFullRefresh = false;
177
+
178
+ try {
179
+ const response = await this.http.evaluate({
180
+ env: this.options.env,
181
+ context: this.context,
182
+ flagKeys: keys,
183
+ });
184
+ this.applyEvaluate(response);
185
+
186
+ if (!this.status.ready) {
187
+ this.status.ready = true;
188
+ this.emitter.emit("ready");
189
+ }
190
+
191
+ this.emitter.emit("update");
192
+ } catch (error) {
193
+ this.status.error = error as Error;
194
+ this.emitter.emit("error", error);
195
+ } finally {
196
+ this.refreshInFlight = undefined;
197
+ }
198
+
199
+ if (this.pendingFullRefresh || this.pendingFlagKeys.size > 0) {
200
+ return this.refresh();
201
+ }
202
+ })();
203
+
204
+ return this.refreshInFlight;
167
205
  }
168
206
 
169
207
  private applyEvaluate(response: SdkEvaluateResponse) {
170
- this.evaluations.clear();
171
208
  for (const [key, evaluation] of Object.entries(response.flags)) {
172
209
  this.evaluations.set(key, evaluation);
173
210
  }