@openfeature/server-sdk 1.19.0 → 1.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +80 -3
- package/dist/cjs/index.js +584 -39
- package/dist/cjs/index.js.map +4 -4
- package/dist/esm/index.js +560 -24
- package/dist/esm/index.js.map +4 -4
- package/dist/types.d.ts +146 -3
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -170,6 +170,550 @@ var InMemoryProvider = class {
|
|
|
170
170
|
}
|
|
171
171
|
};
|
|
172
172
|
|
|
173
|
+
// src/provider/multi-provider/multi-provider.ts
|
|
174
|
+
import { DefaultLogger, ErrorCode as ErrorCode3, GeneralError as GeneralError4, StandardResolutionReasons as StandardResolutionReasons2 } from "@openfeature/core";
|
|
175
|
+
|
|
176
|
+
// src/events/open-feature-event-emitter.ts
|
|
177
|
+
import { GenericEventEmitter } from "@openfeature/core";
|
|
178
|
+
import { EventEmitter } from "node:events";
|
|
179
|
+
var OpenFeatureEventEmitter = class extends GenericEventEmitter {
|
|
180
|
+
constructor() {
|
|
181
|
+
super();
|
|
182
|
+
this.eventEmitter = new EventEmitter({ captureRejections: true });
|
|
183
|
+
this.eventEmitter.on("error", (err) => {
|
|
184
|
+
var _a;
|
|
185
|
+
(_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/provider/multi-provider/errors.ts
|
|
191
|
+
import { GeneralError as GeneralError2, OpenFeatureError as OpenFeatureError3 } from "@openfeature/core";
|
|
192
|
+
var ErrorWithCode = class extends OpenFeatureError3 {
|
|
193
|
+
constructor(code, message) {
|
|
194
|
+
super(message);
|
|
195
|
+
this.code = code;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
var AggregateError = class extends GeneralError2 {
|
|
199
|
+
constructor(message, originalErrors) {
|
|
200
|
+
super(message);
|
|
201
|
+
this.originalErrors = originalErrors;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
var constructAggregateError = (providerErrors) => {
|
|
205
|
+
const errorsWithSource = providerErrors.map(({ providerName, error }) => {
|
|
206
|
+
return { source: providerName, error };
|
|
207
|
+
}).flat();
|
|
208
|
+
return new AggregateError(
|
|
209
|
+
`Provider errors occurred: ${errorsWithSource[0].source}: ${errorsWithSource[0].error}`,
|
|
210
|
+
errorsWithSource
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
var throwAggregateErrorFromPromiseResults = (result, providerEntries) => {
|
|
214
|
+
const errors = result.map((r, i) => {
|
|
215
|
+
if (r.status === "rejected") {
|
|
216
|
+
return { error: r.reason, providerName: providerEntries[i].name };
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}).filter((val) => Boolean(val));
|
|
220
|
+
if (errors.length) {
|
|
221
|
+
throw constructAggregateError(errors);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// src/provider/multi-provider/hook-executor.ts
|
|
226
|
+
var HookExecutor = class {
|
|
227
|
+
constructor(logger) {
|
|
228
|
+
this.logger = logger;
|
|
229
|
+
}
|
|
230
|
+
beforeHooks(hooks, hookContext, hints) {
|
|
231
|
+
return __async(this, null, function* () {
|
|
232
|
+
var _a;
|
|
233
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
234
|
+
Object.freeze(hookContext);
|
|
235
|
+
Object.assign(hookContext.context, __spreadValues({}, yield (_a = hook == null ? void 0 : hook.before) == null ? void 0 : _a.call(hook, hookContext, Object.freeze(hints))));
|
|
236
|
+
}
|
|
237
|
+
return Object.freeze(hookContext.context);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
afterHooks(hooks, hookContext, evaluationDetails, hints) {
|
|
241
|
+
return __async(this, null, function* () {
|
|
242
|
+
var _a;
|
|
243
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
244
|
+
yield (_a = hook == null ? void 0 : hook.after) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, hints);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
errorHooks(hooks, hookContext, err, hints) {
|
|
249
|
+
return __async(this, null, function* () {
|
|
250
|
+
var _a;
|
|
251
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
252
|
+
try {
|
|
253
|
+
yield (_a = hook == null ? void 0 : hook.error) == null ? void 0 : _a.call(hook, hookContext, err, hints);
|
|
254
|
+
} catch (err2) {
|
|
255
|
+
this.logger.error(`Unhandled error during 'error' hook: ${err2}`);
|
|
256
|
+
if (err2 instanceof Error) {
|
|
257
|
+
this.logger.error(err2.stack);
|
|
258
|
+
}
|
|
259
|
+
this.logger.error(err2 == null ? void 0 : err2.stack);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
finallyHooks(hooks, hookContext, evaluationDetails, hints) {
|
|
265
|
+
return __async(this, null, function* () {
|
|
266
|
+
var _a;
|
|
267
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
268
|
+
try {
|
|
269
|
+
yield (_a = hook == null ? void 0 : hook.finally) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, hints);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
this.logger.error(`Unhandled error during 'finally' hook: ${err}`);
|
|
272
|
+
if (err instanceof Error) {
|
|
273
|
+
this.logger.error(err.stack);
|
|
274
|
+
}
|
|
275
|
+
this.logger.error(err == null ? void 0 : err.stack);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// src/events/events.ts
|
|
283
|
+
import { ServerProviderEvents } from "@openfeature/core";
|
|
284
|
+
|
|
285
|
+
// src/provider/multi-provider/status-tracker.ts
|
|
286
|
+
var StatusTracker = class {
|
|
287
|
+
constructor(events) {
|
|
288
|
+
this.events = events;
|
|
289
|
+
this.providerStatuses = {};
|
|
290
|
+
}
|
|
291
|
+
wrapEventHandler(providerEntry) {
|
|
292
|
+
var _a, _b, _c, _d;
|
|
293
|
+
const provider = providerEntry.provider;
|
|
294
|
+
(_a = provider.events) == null ? void 0 : _a.addHandler(ServerProviderEvents.Error, (details) => {
|
|
295
|
+
this.changeProviderStatus(providerEntry.name, ServerProviderStatus.ERROR, details);
|
|
296
|
+
});
|
|
297
|
+
(_b = provider.events) == null ? void 0 : _b.addHandler(ServerProviderEvents.Stale, (details) => {
|
|
298
|
+
this.changeProviderStatus(providerEntry.name, ServerProviderStatus.STALE, details);
|
|
299
|
+
});
|
|
300
|
+
(_c = provider.events) == null ? void 0 : _c.addHandler(ServerProviderEvents.ConfigurationChanged, (details) => {
|
|
301
|
+
this.events.emit(ServerProviderEvents.ConfigurationChanged, details);
|
|
302
|
+
});
|
|
303
|
+
(_d = provider.events) == null ? void 0 : _d.addHandler(ServerProviderEvents.Ready, (details) => {
|
|
304
|
+
this.changeProviderStatus(providerEntry.name, ServerProviderStatus.READY, details);
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
providerStatus(name) {
|
|
308
|
+
return this.providerStatuses[name];
|
|
309
|
+
}
|
|
310
|
+
getStatusFromProviderStatuses() {
|
|
311
|
+
const statuses = Object.values(this.providerStatuses);
|
|
312
|
+
if (statuses.includes(ServerProviderStatus.FATAL)) {
|
|
313
|
+
return ServerProviderStatus.FATAL;
|
|
314
|
+
} else if (statuses.includes(ServerProviderStatus.NOT_READY)) {
|
|
315
|
+
return ServerProviderStatus.NOT_READY;
|
|
316
|
+
} else if (statuses.includes(ServerProviderStatus.ERROR)) {
|
|
317
|
+
return ServerProviderStatus.ERROR;
|
|
318
|
+
} else if (statuses.includes(ServerProviderStatus.STALE)) {
|
|
319
|
+
return ServerProviderStatus.STALE;
|
|
320
|
+
}
|
|
321
|
+
return ServerProviderStatus.READY;
|
|
322
|
+
}
|
|
323
|
+
changeProviderStatus(name, status, details) {
|
|
324
|
+
const currentStatus = this.getStatusFromProviderStatuses();
|
|
325
|
+
this.providerStatuses[name] = status;
|
|
326
|
+
const newStatus = this.getStatusFromProviderStatuses();
|
|
327
|
+
if (currentStatus !== newStatus) {
|
|
328
|
+
if (newStatus === ServerProviderStatus.FATAL || newStatus === ServerProviderStatus.ERROR) {
|
|
329
|
+
this.events.emit(ServerProviderEvents.Error, details);
|
|
330
|
+
} else if (newStatus === ServerProviderStatus.STALE) {
|
|
331
|
+
this.events.emit(ServerProviderEvents.Stale, details);
|
|
332
|
+
} else if (newStatus === ServerProviderStatus.READY) {
|
|
333
|
+
this.events.emit(ServerProviderEvents.Ready, details);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// src/provider/multi-provider/strategies/base-evaluation-strategy.ts
|
|
340
|
+
var BaseEvaluationStrategy = class {
|
|
341
|
+
constructor() {
|
|
342
|
+
this.runMode = "sequential";
|
|
343
|
+
}
|
|
344
|
+
shouldEvaluateThisProvider(strategyContext, _evalContext) {
|
|
345
|
+
if (strategyContext.providerStatus === ServerProviderStatus.NOT_READY || strategyContext.providerStatus === ServerProviderStatus.FATAL) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
shouldEvaluateNextProvider(_strategyContext, _context, _result) {
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
shouldTrackWithThisProvider(strategyContext, _context, _trackingEventName, _trackingEventDetails) {
|
|
354
|
+
if (strategyContext.providerStatus === ServerProviderStatus.NOT_READY || strategyContext.providerStatus === ServerProviderStatus.FATAL) {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
hasError(resolution) {
|
|
360
|
+
return "thrownError" in resolution || !!resolution.details.errorCode;
|
|
361
|
+
}
|
|
362
|
+
hasErrorWithCode(resolution, code) {
|
|
363
|
+
var _a;
|
|
364
|
+
return "thrownError" in resolution ? ((_a = resolution.thrownError) == null ? void 0 : _a.code) === code : resolution.details.errorCode === code;
|
|
365
|
+
}
|
|
366
|
+
collectProviderErrors(resolutions) {
|
|
367
|
+
var _a;
|
|
368
|
+
const errors = [];
|
|
369
|
+
for (const resolution of resolutions) {
|
|
370
|
+
if ("thrownError" in resolution) {
|
|
371
|
+
errors.push({ providerName: resolution.providerName, error: resolution.thrownError });
|
|
372
|
+
} else if (resolution.details.errorCode) {
|
|
373
|
+
errors.push({
|
|
374
|
+
providerName: resolution.providerName,
|
|
375
|
+
error: new ErrorWithCode(resolution.details.errorCode, (_a = resolution.details.errorMessage) != null ? _a : "unknown error")
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return { errors };
|
|
380
|
+
}
|
|
381
|
+
resolutionToFinalResult(resolution) {
|
|
382
|
+
return { details: resolution.details, provider: resolution.provider, providerName: resolution.providerName };
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// src/provider/multi-provider/strategies/first-match-strategy.ts
|
|
387
|
+
import { ErrorCode as ErrorCode2 } from "@openfeature/core";
|
|
388
|
+
var FirstMatchStrategy = class extends BaseEvaluationStrategy {
|
|
389
|
+
shouldEvaluateNextProvider(strategyContext, context, result) {
|
|
390
|
+
if (this.hasErrorWithCode(result, ErrorCode2.FLAG_NOT_FOUND)) {
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
if (this.hasError(result)) {
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
determineFinalResult(strategyContext, context, resolutions) {
|
|
399
|
+
const finalResolution = resolutions[resolutions.length - 1];
|
|
400
|
+
if (this.hasError(finalResolution)) {
|
|
401
|
+
return this.collectProviderErrors(resolutions);
|
|
402
|
+
}
|
|
403
|
+
return this.resolutionToFinalResult(finalResolution);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// src/provider/multi-provider/strategies/first-successful-strategy.ts
|
|
408
|
+
var FirstSuccessfulStrategy = class extends BaseEvaluationStrategy {
|
|
409
|
+
shouldEvaluateNextProvider(strategyContext, context, result) {
|
|
410
|
+
return this.hasError(result);
|
|
411
|
+
}
|
|
412
|
+
determineFinalResult(strategyContext, context, resolutions) {
|
|
413
|
+
const finalResolution = resolutions[resolutions.length - 1];
|
|
414
|
+
if (this.hasError(finalResolution)) {
|
|
415
|
+
return this.collectProviderErrors(resolutions);
|
|
416
|
+
}
|
|
417
|
+
return this.resolutionToFinalResult(finalResolution);
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// src/provider/multi-provider/strategies/comparison-strategy.ts
|
|
422
|
+
import { GeneralError as GeneralError3 } from "@openfeature/core";
|
|
423
|
+
var ComparisonStrategy = class extends BaseEvaluationStrategy {
|
|
424
|
+
constructor(fallbackProvider, onMismatch) {
|
|
425
|
+
super();
|
|
426
|
+
this.fallbackProvider = fallbackProvider;
|
|
427
|
+
this.onMismatch = onMismatch;
|
|
428
|
+
this.runMode = "parallel";
|
|
429
|
+
}
|
|
430
|
+
determineFinalResult(strategyContext, context, resolutions) {
|
|
431
|
+
var _a;
|
|
432
|
+
let value;
|
|
433
|
+
let fallbackResolution;
|
|
434
|
+
let finalResolution;
|
|
435
|
+
let mismatch = false;
|
|
436
|
+
for (const [i, resolution] of resolutions.entries()) {
|
|
437
|
+
if (this.hasError(resolution)) {
|
|
438
|
+
return this.collectProviderErrors(resolutions);
|
|
439
|
+
}
|
|
440
|
+
if (resolution.provider === this.fallbackProvider) {
|
|
441
|
+
fallbackResolution = resolution;
|
|
442
|
+
}
|
|
443
|
+
if (i === 0) {
|
|
444
|
+
finalResolution = resolution;
|
|
445
|
+
}
|
|
446
|
+
if (typeof value !== "undefined" && value !== resolution.details.value) {
|
|
447
|
+
mismatch = true;
|
|
448
|
+
} else {
|
|
449
|
+
value = resolution.details.value;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (!fallbackResolution) {
|
|
453
|
+
throw new GeneralError3("Fallback provider not found in resolution results");
|
|
454
|
+
}
|
|
455
|
+
if (!finalResolution) {
|
|
456
|
+
throw new GeneralError3("Final resolution not found in resolution results");
|
|
457
|
+
}
|
|
458
|
+
if (mismatch) {
|
|
459
|
+
(_a = this.onMismatch) == null ? void 0 : _a.call(this, resolutions);
|
|
460
|
+
return {
|
|
461
|
+
details: fallbackResolution.details,
|
|
462
|
+
provider: fallbackResolution.provider
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
return this.resolutionToFinalResult(finalResolution);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// src/provider/multi-provider/multi-provider.ts
|
|
470
|
+
var MultiProvider = class _MultiProvider {
|
|
471
|
+
constructor(constructorProviders, evaluationStrategy = new FirstMatchStrategy(), logger = new DefaultLogger()) {
|
|
472
|
+
this.constructorProviders = constructorProviders;
|
|
473
|
+
this.evaluationStrategy = evaluationStrategy;
|
|
474
|
+
this.logger = logger;
|
|
475
|
+
this.runsOn = "server";
|
|
476
|
+
this.events = new OpenFeatureEventEmitter();
|
|
477
|
+
this.hookContexts = /* @__PURE__ */ new WeakMap();
|
|
478
|
+
this.hookHints = /* @__PURE__ */ new WeakMap();
|
|
479
|
+
this.providerEntries = [];
|
|
480
|
+
this.providerEntriesByName = {};
|
|
481
|
+
this.statusTracker = new StatusTracker(this.events);
|
|
482
|
+
this.hookExecutor = new HookExecutor(this.logger);
|
|
483
|
+
this.registerProviders(constructorProviders);
|
|
484
|
+
const aggregateMetadata = Object.keys(this.providerEntriesByName).reduce((acc, name) => {
|
|
485
|
+
return __spreadProps(__spreadValues({}, acc), { [name]: this.providerEntriesByName[name].provider.metadata });
|
|
486
|
+
}, {});
|
|
487
|
+
this.metadata = __spreadProps(__spreadValues({}, aggregateMetadata), {
|
|
488
|
+
name: _MultiProvider.name
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
registerProviders(constructorProviders) {
|
|
492
|
+
var _a, _b;
|
|
493
|
+
const providersByName = {};
|
|
494
|
+
for (const constructorProvider of constructorProviders) {
|
|
495
|
+
const providerName = constructorProvider.provider.metadata.name;
|
|
496
|
+
const candidateName = (_a = constructorProvider.name) != null ? _a : providerName;
|
|
497
|
+
if (constructorProvider.name && providersByName[constructorProvider.name]) {
|
|
498
|
+
throw new Error("Provider names must be unique");
|
|
499
|
+
}
|
|
500
|
+
(_b = providersByName[candidateName]) != null ? _b : providersByName[candidateName] = [];
|
|
501
|
+
providersByName[candidateName].push(constructorProvider.provider);
|
|
502
|
+
}
|
|
503
|
+
for (const name of Object.keys(providersByName)) {
|
|
504
|
+
const useIndexedNames = providersByName[name].length > 1;
|
|
505
|
+
for (let i = 0; i < providersByName[name].length; i++) {
|
|
506
|
+
const indexedName = useIndexedNames ? `${name}-${i + 1}` : name;
|
|
507
|
+
this.providerEntriesByName[indexedName] = { provider: providersByName[name][i], name: indexedName };
|
|
508
|
+
this.providerEntries.push(this.providerEntriesByName[indexedName]);
|
|
509
|
+
this.statusTracker.wrapEventHandler(this.providerEntriesByName[indexedName]);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
Object.freeze(this.providerEntries);
|
|
513
|
+
Object.freeze(this.providerEntriesByName);
|
|
514
|
+
}
|
|
515
|
+
initialize(context) {
|
|
516
|
+
return __async(this, null, function* () {
|
|
517
|
+
const result = yield Promise.allSettled(
|
|
518
|
+
this.providerEntries.map((provider) => {
|
|
519
|
+
var _a, _b;
|
|
520
|
+
return (_b = (_a = provider.provider).initialize) == null ? void 0 : _b.call(_a, context);
|
|
521
|
+
})
|
|
522
|
+
);
|
|
523
|
+
throwAggregateErrorFromPromiseResults(result, this.providerEntries);
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
onClose() {
|
|
527
|
+
return __async(this, null, function* () {
|
|
528
|
+
const result = yield Promise.allSettled(this.providerEntries.map((provider) => {
|
|
529
|
+
var _a, _b;
|
|
530
|
+
return (_b = (_a = provider.provider).onClose) == null ? void 0 : _b.call(_a);
|
|
531
|
+
}));
|
|
532
|
+
throwAggregateErrorFromPromiseResults(result, this.providerEntries);
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
resolveBooleanEvaluation(flagKey, defaultValue, context) {
|
|
536
|
+
return this.flagResolutionProxy(flagKey, "boolean", defaultValue, context);
|
|
537
|
+
}
|
|
538
|
+
resolveStringEvaluation(flagKey, defaultValue, context) {
|
|
539
|
+
return this.flagResolutionProxy(flagKey, "string", defaultValue, context);
|
|
540
|
+
}
|
|
541
|
+
resolveNumberEvaluation(flagKey, defaultValue, context) {
|
|
542
|
+
return this.flagResolutionProxy(flagKey, "number", defaultValue, context);
|
|
543
|
+
}
|
|
544
|
+
resolveObjectEvaluation(flagKey, defaultValue, context) {
|
|
545
|
+
return this.flagResolutionProxy(flagKey, "object", defaultValue, context);
|
|
546
|
+
}
|
|
547
|
+
flagResolutionProxy(flagKey, flagType, defaultValue, context) {
|
|
548
|
+
return __async(this, null, function* () {
|
|
549
|
+
var _a;
|
|
550
|
+
const hookContext = this.hookContexts.get(context);
|
|
551
|
+
const hookHints = this.hookHints.get(context);
|
|
552
|
+
if (!hookContext || !hookHints) {
|
|
553
|
+
throw new GeneralError4("Hook context not available for evaluation");
|
|
554
|
+
}
|
|
555
|
+
const tasks = [];
|
|
556
|
+
for (const providerEntry of this.providerEntries) {
|
|
557
|
+
const task = this.evaluateProviderEntry(
|
|
558
|
+
flagKey,
|
|
559
|
+
flagType,
|
|
560
|
+
defaultValue,
|
|
561
|
+
providerEntry,
|
|
562
|
+
hookContext,
|
|
563
|
+
hookHints,
|
|
564
|
+
context
|
|
565
|
+
);
|
|
566
|
+
tasks.push(task);
|
|
567
|
+
if (this.evaluationStrategy.runMode === "sequential") {
|
|
568
|
+
const [shouldEvaluateNext] = yield task;
|
|
569
|
+
if (!shouldEvaluateNext) {
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const results = yield Promise.all(tasks);
|
|
575
|
+
const resolutions = results.map(([, resolution]) => resolution).filter((r) => Boolean(r));
|
|
576
|
+
const finalResult = this.evaluationStrategy.determineFinalResult({ flagKey, flagType }, context, resolutions);
|
|
577
|
+
if ((_a = finalResult.errors) == null ? void 0 : _a.length) {
|
|
578
|
+
throw constructAggregateError(finalResult.errors);
|
|
579
|
+
}
|
|
580
|
+
if (!finalResult.details) {
|
|
581
|
+
throw new GeneralError4("No result was returned from any provider");
|
|
582
|
+
}
|
|
583
|
+
return finalResult.details;
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
evaluateProviderEntry(flagKey, flagType, defaultValue, providerEntry, hookContext, hookHints, context) {
|
|
587
|
+
return __async(this, null, function* () {
|
|
588
|
+
let evaluationResult = void 0;
|
|
589
|
+
const provider = providerEntry.provider;
|
|
590
|
+
const strategyContext = {
|
|
591
|
+
flagKey,
|
|
592
|
+
flagType,
|
|
593
|
+
provider,
|
|
594
|
+
providerName: providerEntry.name,
|
|
595
|
+
providerStatus: this.statusTracker.providerStatus(providerEntry.name)
|
|
596
|
+
};
|
|
597
|
+
if (!this.evaluationStrategy.shouldEvaluateThisProvider(strategyContext, context)) {
|
|
598
|
+
return [true, null];
|
|
599
|
+
}
|
|
600
|
+
let resolution;
|
|
601
|
+
try {
|
|
602
|
+
evaluationResult = yield this.evaluateProviderAndHooks(flagKey, defaultValue, provider, hookContext, hookHints);
|
|
603
|
+
resolution = {
|
|
604
|
+
details: evaluationResult,
|
|
605
|
+
provider,
|
|
606
|
+
providerName: providerEntry.name
|
|
607
|
+
};
|
|
608
|
+
} catch (error) {
|
|
609
|
+
resolution = {
|
|
610
|
+
thrownError: error,
|
|
611
|
+
provider,
|
|
612
|
+
providerName: providerEntry.name
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
return [
|
|
616
|
+
this.evaluationStrategy.runMode === "sequential" ? this.evaluationStrategy.shouldEvaluateNextProvider(strategyContext, context, resolution) : true,
|
|
617
|
+
resolution
|
|
618
|
+
];
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
evaluateProviderAndHooks(flagKey, defaultValue, provider, hookContext, hookHints) {
|
|
622
|
+
return __async(this, null, function* () {
|
|
623
|
+
var _a;
|
|
624
|
+
let providerContext = void 0;
|
|
625
|
+
let evaluationDetails;
|
|
626
|
+
const hookContextCopy = __spreadProps(__spreadValues({}, hookContext), { context: __spreadValues({}, hookContext.context) });
|
|
627
|
+
try {
|
|
628
|
+
providerContext = yield this.hookExecutor.beforeHooks(provider.hooks, hookContextCopy, hookHints);
|
|
629
|
+
const resolutionDetails = yield this.callProviderResolve(
|
|
630
|
+
provider,
|
|
631
|
+
flagKey,
|
|
632
|
+
defaultValue,
|
|
633
|
+
providerContext || {}
|
|
634
|
+
);
|
|
635
|
+
evaluationDetails = __spreadProps(__spreadValues({}, resolutionDetails), {
|
|
636
|
+
flagMetadata: Object.freeze((_a = resolutionDetails.flagMetadata) != null ? _a : {}),
|
|
637
|
+
flagKey
|
|
638
|
+
});
|
|
639
|
+
yield this.hookExecutor.afterHooks(provider.hooks, hookContextCopy, evaluationDetails, hookHints);
|
|
640
|
+
} catch (error) {
|
|
641
|
+
yield this.hookExecutor.errorHooks(provider.hooks, hookContextCopy, error, hookHints);
|
|
642
|
+
evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, error);
|
|
643
|
+
}
|
|
644
|
+
yield this.hookExecutor.finallyHooks(provider.hooks, hookContextCopy, evaluationDetails, hookHints);
|
|
645
|
+
return evaluationDetails;
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
callProviderResolve(provider, flagKey, defaultValue, context) {
|
|
649
|
+
return __async(this, null, function* () {
|
|
650
|
+
switch (typeof defaultValue) {
|
|
651
|
+
case "string":
|
|
652
|
+
return yield provider.resolveStringEvaluation(flagKey, defaultValue, context, this.logger);
|
|
653
|
+
case "number":
|
|
654
|
+
return yield provider.resolveNumberEvaluation(flagKey, defaultValue, context, this.logger);
|
|
655
|
+
case "object":
|
|
656
|
+
return yield provider.resolveObjectEvaluation(flagKey, defaultValue, context, this.logger);
|
|
657
|
+
case "boolean":
|
|
658
|
+
return yield provider.resolveBooleanEvaluation(flagKey, defaultValue, context, this.logger);
|
|
659
|
+
default:
|
|
660
|
+
throw new GeneralError4("Invalid flag evaluation type");
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
get hooks() {
|
|
665
|
+
return [
|
|
666
|
+
{
|
|
667
|
+
before: (hookContext, hints) => __async(this, null, function* () {
|
|
668
|
+
this.hookContexts.set(hookContext.context, hookContext);
|
|
669
|
+
this.hookHints.set(hookContext.context, hints != null ? hints : {});
|
|
670
|
+
return hookContext.context;
|
|
671
|
+
})
|
|
672
|
+
}
|
|
673
|
+
];
|
|
674
|
+
}
|
|
675
|
+
track(trackingEventName, context, trackingEventDetails) {
|
|
676
|
+
var _a, _b;
|
|
677
|
+
for (const providerEntry of this.providerEntries) {
|
|
678
|
+
if (!providerEntry.provider.track) {
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
const strategyContext = {
|
|
682
|
+
provider: providerEntry.provider,
|
|
683
|
+
providerName: providerEntry.name,
|
|
684
|
+
providerStatus: this.statusTracker.providerStatus(providerEntry.name)
|
|
685
|
+
};
|
|
686
|
+
if (this.evaluationStrategy.shouldTrackWithThisProvider(
|
|
687
|
+
strategyContext,
|
|
688
|
+
context,
|
|
689
|
+
trackingEventName,
|
|
690
|
+
trackingEventDetails
|
|
691
|
+
)) {
|
|
692
|
+
try {
|
|
693
|
+
(_b = (_a = providerEntry.provider).track) == null ? void 0 : _b.call(_a, trackingEventName, context, trackingEventDetails);
|
|
694
|
+
} catch (error) {
|
|
695
|
+
this.logger.error(
|
|
696
|
+
`Error tracking event "${trackingEventName}" with provider "${providerEntry.name}":`,
|
|
697
|
+
error
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
getErrorEvaluationDetails(flagKey, defaultValue, err, flagMetadata = {}) {
|
|
704
|
+
const errorMessage = err == null ? void 0 : err.message;
|
|
705
|
+
const errorCode = (err == null ? void 0 : err.code) || ErrorCode3.GENERAL;
|
|
706
|
+
return {
|
|
707
|
+
errorCode,
|
|
708
|
+
errorMessage,
|
|
709
|
+
value: defaultValue,
|
|
710
|
+
reason: StandardResolutionReasons2.ERROR,
|
|
711
|
+
flagMetadata: Object.freeze(flagMetadata),
|
|
712
|
+
flagKey
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
|
|
173
717
|
// src/open-feature.ts
|
|
174
718
|
import {
|
|
175
719
|
OpenFeatureCommonAPI,
|
|
@@ -180,11 +724,11 @@ import {
|
|
|
180
724
|
|
|
181
725
|
// src/client/internal/open-feature-client.ts
|
|
182
726
|
import {
|
|
183
|
-
ErrorCode as
|
|
727
|
+
ErrorCode as ErrorCode4,
|
|
184
728
|
ProviderFatalError,
|
|
185
729
|
ProviderNotReadyError,
|
|
186
730
|
SafeLogger,
|
|
187
|
-
StandardResolutionReasons as
|
|
731
|
+
StandardResolutionReasons as StandardResolutionReasons3,
|
|
188
732
|
instantiateErrorByErrorCode,
|
|
189
733
|
statusMatchesEvent,
|
|
190
734
|
MapHookData
|
|
@@ -384,14 +928,14 @@ var OpenFeatureClient = class {
|
|
|
384
928
|
beforeHooks(hooks, hookContexts, mergedContext, options) {
|
|
385
929
|
return __async(this, null, function* () {
|
|
386
930
|
var _a;
|
|
387
|
-
|
|
931
|
+
const accumulatedContext = mergedContext;
|
|
388
932
|
for (const [index, hook] of hooks.entries()) {
|
|
389
933
|
const hookContextIndex = hooks.length - 1 - index;
|
|
390
934
|
const hookContext = hookContexts[hookContextIndex];
|
|
391
935
|
Object.assign(hookContext.context, accumulatedContext);
|
|
392
936
|
const hookResult = yield (_a = hook == null ? void 0 : hook.before) == null ? void 0 : _a.call(hook, hookContext, Object.freeze(options.hookHints));
|
|
393
937
|
if (hookResult) {
|
|
394
|
-
|
|
938
|
+
Object.assign(accumulatedContext, hookResult);
|
|
395
939
|
for (let i = 0; i < hooks.length; i++) {
|
|
396
940
|
Object.assign(hookContexts[hookContextIndex].context, accumulatedContext);
|
|
397
941
|
}
|
|
@@ -464,35 +1008,18 @@ var OpenFeatureClient = class {
|
|
|
464
1008
|
}
|
|
465
1009
|
getErrorEvaluationDetails(flagKey, defaultValue, err, flagMetadata = {}) {
|
|
466
1010
|
const errorMessage = err == null ? void 0 : err.message;
|
|
467
|
-
const errorCode = (err == null ? void 0 : err.code) ||
|
|
1011
|
+
const errorCode = (err == null ? void 0 : err.code) || ErrorCode4.GENERAL;
|
|
468
1012
|
return {
|
|
469
1013
|
errorCode,
|
|
470
1014
|
errorMessage,
|
|
471
1015
|
value: defaultValue,
|
|
472
|
-
reason:
|
|
1016
|
+
reason: StandardResolutionReasons3.ERROR,
|
|
473
1017
|
flagMetadata: Object.freeze(flagMetadata),
|
|
474
1018
|
flagKey
|
|
475
1019
|
};
|
|
476
1020
|
}
|
|
477
1021
|
};
|
|
478
1022
|
|
|
479
|
-
// src/events/open-feature-event-emitter.ts
|
|
480
|
-
import { GenericEventEmitter } from "@openfeature/core";
|
|
481
|
-
import { EventEmitter } from "node:events";
|
|
482
|
-
var OpenFeatureEventEmitter = class extends GenericEventEmitter {
|
|
483
|
-
constructor() {
|
|
484
|
-
super();
|
|
485
|
-
this.eventEmitter = new EventEmitter({ captureRejections: true });
|
|
486
|
-
this.eventEmitter.on("error", (err) => {
|
|
487
|
-
var _a;
|
|
488
|
-
(_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err);
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
// src/events/events.ts
|
|
494
|
-
import { ServerProviderEvents } from "@openfeature/core";
|
|
495
|
-
|
|
496
1023
|
// src/transaction-context/no-op-transaction-context-propagator.ts
|
|
497
1024
|
var NoopTransactionContextPropagator = class {
|
|
498
1025
|
getTransactionContext() {
|
|
@@ -637,14 +1164,23 @@ var OpenFeature = OpenFeatureAPI.getInstance();
|
|
|
637
1164
|
// src/index.ts
|
|
638
1165
|
export * from "@openfeature/core";
|
|
639
1166
|
export {
|
|
1167
|
+
AggregateError,
|
|
640
1168
|
AsyncLocalStorageTransactionContextPropagator,
|
|
1169
|
+
BaseEvaluationStrategy,
|
|
1170
|
+
ComparisonStrategy,
|
|
1171
|
+
ErrorWithCode,
|
|
1172
|
+
FirstMatchStrategy,
|
|
1173
|
+
FirstSuccessfulStrategy,
|
|
641
1174
|
InMemoryProvider,
|
|
1175
|
+
MultiProvider,
|
|
642
1176
|
NOOP_PROVIDER,
|
|
643
1177
|
NOOP_TRANSACTION_CONTEXT_PROPAGATOR,
|
|
644
1178
|
OpenFeature,
|
|
645
1179
|
OpenFeatureAPI,
|
|
646
1180
|
OpenFeatureEventEmitter,
|
|
647
1181
|
ServerProviderEvents as ProviderEvents,
|
|
648
|
-
ServerProviderStatus as ProviderStatus
|
|
1182
|
+
ServerProviderStatus as ProviderStatus,
|
|
1183
|
+
constructAggregateError,
|
|
1184
|
+
throwAggregateErrorFromPromiseResults
|
|
649
1185
|
};
|
|
650
1186
|
//# sourceMappingURL=index.js.map
|